001 /*BEGIN_COPYRIGHT_BLOCK
002 *
003 * Copyright (c) 2001-2010, JavaPLT group at Rice University (drjava@rice.edu)
004 * All rights reserved.
005 *
006 * Redistribution and use in source and binary forms, with or without
007 * modification, are permitted provided that the following conditions are met:
008 * * Redistributions of source code must retain the above copyright
009 * notice, this list of conditions and the following disclaimer.
010 * * Redistributions in binary form must reproduce the above copyright
011 * notice, this list of conditions and the following disclaimer in the
012 * documentation and/or other materials provided with the distribution.
013 * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
014 * names of its contributors may be used to endorse or promote products
015 * derived from this software without specific prior written permission.
016 *
017 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
018 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
019 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
020 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
021 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
022 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
023 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
024 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
025 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
026 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
027 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028 *
029 * This software is Open Source Initiative approved Open Source Software.
030 * Open Source Initative Approved is a trademark of the Open Source Initiative.
031 *
032 * This file is part of DrJava. Download the current version of this project
033 * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
034 *
035 * END_COPYRIGHT_BLOCK*/
036
037 package edu.rice.cs.javalanglevels;
038
039 import org.objectweb.asm.*;
040 import edu.rice.cs.javalanglevels.tree.*;
041 import edu.rice.cs.javalanglevels.tree.Type; // resolve ambiguity
042 import edu.rice.cs.javalanglevels.parser.JExprParser;
043 import edu.rice.cs.javalanglevels.parser.ParseException;
044 import edu.rice.cs.javalanglevels.util.Log;
045 import edu.rice.cs.javalanglevels.util.Utilities;
046 import java.util.*;
047 import java.io.*;
048 import edu.rice.cs.plt.reflect.JavaVersion;
049 import edu.rice.cs.plt.iter.*;
050 import edu.rice.cs.plt.io.IOUtil;
051
052 import junit.framework.TestCase;
053
054 /** Top-level Language Level Visitor that implements the constraint checking and symbol table building that is common to
055 * first pass processing for the Functional and FullJava levels. There are two major complications in performing this
056 * pass. First, references to symbols appear in the signatures of type (class/interface) definitions that have not yet
057 * been defined. In the symbol table, the binding of these references must be deferred until a fixup list is executed
058 * after the first pass visit has finished. This visitor and its descendants maintain a FixUp list for this purpose.
059 * Second, the loading of signature information into the symbol table (called "resolving" in this documentation) is
060 * deferred for some symbols. A dummy entry called a "continuation" is created in the symbol table for each such
061 * symbol.
062 */
063 public class LanguageLevelVisitor extends JExpressionIFPrunableDepthFirstVisitor {
064
065 public static final ModifiersAndVisibility PUBLIC_MAV =
066 new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"});
067 public static final ModifiersAndVisibility PROTECTED_MAV =
068 new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"protected"});
069 public static final ModifiersAndVisibility PRIVATE_MAV =
070 new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"private"});
071 public static final ModifiersAndVisibility PACKAGE_MAV =
072 new ModifiersAndVisibility(SourceInfo.NONE, new String[0]);
073 public static final ModifiersAndVisibility FINAL_MAV =
074 new ModifiersAndVisibility(SourceInfo.NONE, new String[]{"final"});
075
076 /** Errors we have encountered during this pass: string is the text of the error, JExpressionIF is the part of
077 * the AST where the error occurs. */
078 protected static LinkedList<Pair<String, JExpressionIF>> errors;
079
080 /** Stores the classes we have referenced, and all their information, once they are resolved. Bound to static field
081 * LanguageLevelConverter.symboltable. */
082 public final Symboltable symbolTable;
083
084 /** A table of the names of symbols for which dummy symbol entries (continuations) have been created and resolution
085 * has been deferred. In some cases (symbols subsequently defined in a file being converted), resolution occurs
086 * during execution. TODO: make this field dynamic. */
087 static Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>> continuations;
088
089 /** A table of the commands to be executed after this visitation is complete; these commands fill in missing objects
090 * in the symbolTable (which were not available at the time the containing object was constructed.
091 * TODO: make this field dynamic. */
092 static LinkedList<Command> fixUps;
093
094 // TODO: !!! This field appears vestigal; it does not appear to affect execution. Eliminate it
095 /* A list of other files that are being visited. If the SourceFile is not null, then the source file was
096 * visited as opposed to the class file. This info is used by LanguageLevelConverter in DrJava.
097 * We keep the LLV rather than the file, because the LLV has a file, and we need some other information
098 * stored in the LLV to properly look up the file.
099 */
100 static LinkedList<Pair<LanguageLevelVisitor, SourceFile>> visitedFiles;
101
102 /**True once we have encountered an error we cannot recover from. TODO: ??? recover in what sense. */
103 static boolean _errorAdded;
104
105 /** The source file that is being compiled */
106 File _file;
107
108 /** The package of the current file */
109 String _package;
110
111 /** The name of the current enclosing class. This is null for a top-level visitor but is bound to an object in
112 * IntermediateVisitor/FullJavaVisitor. The package name prefix is included. Inner class names include class
113 * qualifiers. */
114 String _enclosingClassName;
115
116 /** A list of file names (classes) imported by the current file. TODO: change the name to _importedClasses or
117 * _importedTypes. */
118 LinkedList<String> _importedFiles;
119
120 /** A list of package names imported by the current file. */
121 LinkedList<String> _importedPackages;
122
123 // A mapping from in scope generic type parameters to their bounds.
124 public HashMap<String, SymbolData> _genericTypes;
125
126 /** The fully qualified class names for top level ClassDefs and InterfaceDefs in the current file that have not
127 * yet been defined. This filed is used to optimize symbol table lookups obviating the need for some fixups. */
128 HashSet<String> _classesInThisFile;
129
130 // /** The inner classes in this class body; null if this is not within a class body. */
131 // HashSet<String> _innerClassesInThisBody;
132
133 protected static final Log _log = new Log("LLConverter.txt", false);
134
135 /** This constructor is called from the subclasses of LanguageLevelVisitor.
136 * @param file The File corresponding to the source file we are visiting
137 * @param packageName The name of the package corresponding to the file
138 * @param importedFiles The list of files (classes) imported by this source file
139 * @param importedPackages The list of packages imported by this source file
140 * @param classesInThisFile The list of names of classes defined in this file
141 * @param continuations The table of classes we have encountered but still need to resolve
142 */
143 public LanguageLevelVisitor(File file,
144 String packageName,
145 String enclosingClassName,
146 LinkedList<String> importedFiles,
147 LinkedList<String> importedPackages,
148 HashSet<String> classesInThisFile,
149 Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>> continuations,
150 LinkedList<Command> fixUps,
151 HashMap<String, SymbolData> genericTypes) {
152 _file = file;
153 _package = packageName;
154 _enclosingClassName = enclosingClassName;
155 if (_enclosingClassName != null && _enclosingClassName.startsWith("null")) assert false;
156 _importedFiles = importedFiles;
157 _importedPackages = importedPackages;
158 _classesInThisFile = classesInThisFile;
159 // _innerClassesInThisBody = new HashSet<String>();
160 this.continuations = continuations;
161 this.fixUps = fixUps;
162 _genericTypes = genericTypes;
163
164 symbolTable = LanguageLevelConverter.symbolTable;
165
166 assert fixUps != null;
167 assert _genericTypes != null;
168
169 // Ensure that the imported packages include "java.lang"
170 if (! _importedPackages.contains("java.lang")) _importedPackages.addFirst("java.lang");
171 // Ensure that the symbol table contains the essential types; TODO: this is kludge; fix it !!!
172 LanguageLevelConverter.loadSymbolTable();
173 }
174
175 /** This constructor is used only in testing.
176 * @param file The File corresponding to the source file we are visiting
177 * @param packageName The name of the package corresponding to the file
178 * @param importedFiles The list of files (classes) imported by this source file
179 * @param importedPackages The list of packages imported by this source file
180 * @param classesInThisFile The list of names of classes defined in this file
181 * @param continuations The table of classes we have encountered but still need to resolve
182 */
183 public LanguageLevelVisitor(File file,
184 String packageName,
185 String enclosingClassName,
186 LinkedList<String> importedFiles,
187 LinkedList<String> importedPackages,
188 HashSet<String> classesInThisFile,
189 Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>> continuations,
190 LinkedList<Command> fixUps) {
191 this(file, packageName, enclosingClassName, importedFiles, importedPackages, classesInThisFile, continuations, fixUps,
192 new HashMap<String, SymbolData>());
193 }
194
195 /* Reset the nonStatic fields of this visitor. Used during testing. */
196 protected void _resetNonStaticFields() {
197 _file = new File("");
198 _enclosingClassName = null;
199 _package = "";
200 _importedFiles = new LinkedList<String>();
201 _importedPackages = new LinkedList<String>();
202 }
203
204 /** @return the accessor name corresponding to given field name. */
205 public static String getFieldAccessorName(String name) { return name; }
206
207 /**@return the source file*/
208 public File getFile() { return _file; }
209
210 /** @return true if this data is a constructor, i.e., it is a method data, its name and return type are the same, and
211 * its return type matches its enclosing sd.
212 */
213 protected boolean isConstructor(Data d) {
214 if ( !(d instanceof MethodData) ) return false;
215 MethodData md = (MethodData) d;
216 SymbolData rt = md.getReturnType();
217 SymbolData sd = md.getSymbolData(); // if this is a constructor, sd must be a defined symbol
218
219 return (rt != null && sd != null && rt.getName().indexOf(md.getName()) != -1 && rt == sd);
220 }
221
222 /** Factory method that constructs an appropriate class body visitor for this visitor class (either
223 * ClassBodyIntermediateVisitor or ClassBodyFullJavaVisitor). This class and method should be abstract, but the LLV
224 * class is used concretely in testing and elswhere. The default choice is ClassBodyFullJavaVisitor. */
225 public LanguageLevelVisitor newClassBodyVisitor(SymbolData anonSD, String anonName) {
226 return new ClassBodyFullJavaVisitor(anonSD, anonName, _file, _package, _importedFiles, _importedPackages,
227 _classesInThisFile, continuations, fixUps);
228 }
229
230 /** Takes a classname and returns only the final segment of it. This removes all the dots. Returns "" for anonymous
231 * class names. TODO: Do we need to fix this? */
232 public static String getUnqualifiedClassName(String className) {
233 int lastIndexOfDot = className.lastIndexOf('.');
234 if (lastIndexOfDot != -1) {
235 className = className.substring(lastIndexOfDot + 1);
236 }
237 int lastIndexOfDollar = className.lastIndexOf('$');
238 if (lastIndexOfDollar != -1) {
239 className = className.substring(lastIndexOfDollar + 1);
240 }
241 // Remove any leading numbers TODO: why do this? If we encounter a
242 while (className.length() > 0 && Character.isDigit(className.charAt(0))) {
243 className = className.substring(1, className.length());
244 }
245 return className;
246 }
247
248 /** Convert the ReferenceType[] to a String[] with the names of the ReferenceTypes. */
249 protected static String[] referenceType2String(ReferenceType[] rts) {
250 String[] throwStrings = new String[rts.length];
251 for (int i = 0; i < throwStrings.length; i++) {
252 throwStrings[i] = rts[i].getName();
253 }
254 return throwStrings;
255 }
256
257
258 // /** Build a SourceInfo corresponding to the specified class name, with -1 as the
259 // * value for row and column of the start and finish.
260 // */
261 // protected static SourceInfo _makeSourceInfo(String qualifiedClassName) {
262 // return new SourceInfo(new File(qualifiedClassName), -1, -1, -1, -1);
263 // }
264
265 /** Check to see if the specified classname is the name of a fully qualified java library class. */
266 public static boolean isJavaLibraryClass(String className) {
267 return className.startsWith("java.") ||
268 className.startsWith("javax.") ||
269 className.startsWith("org.ietf.") ||
270 className.startsWith("org.omg.") ||
271 className.startsWith("org.w3c.") ||
272 className.startsWith("org.xml.") ||
273 className.startsWith("sun.") ||
274 className.startsWith("junit.framework."); //TODO: help!
275 }
276
277 /** @return true if the specified VariableData overwrites one of the members of the list of VariableDatas
278 * and false otherwise. A VariableData is overwritten if its name is shadowed.
279 */
280 public static boolean isDuplicateVariableData(LinkedList<VariableData> vds, VariableData toInsert) {
281 for (int i = 0; i<vds.size(); i++) {
282 VariableData temp = vds.get(i);
283 if (temp.getName().equals(toInsert.getName())) {
284 return true;
285 }
286 }
287 return false;
288 }
289
290 /* Creates a new ArrayData of the element type specified by eltSd with llv and si as the corresponding
291 * LanguageLevelVisitor and SourceInfo and enters it in the SymbolTable. NOTE: this code erroneously
292 * marks an array symbol as a non-continuations when the element symbol is still a continuation!
293 */
294 public ArrayData defineArraySymbolData(SymbolData eltSd, LanguageLevelVisitor llv, SourceInfo si) {
295 ArrayData arraySd = new ArrayData(eltSd, llv, si); // sets _ isContinuation to false!
296 // System.err.println("##### Defining the array symbol " + arraySd.getName());
297 symbolTable.put(arraySd.getName(), arraySd);
298 return arraySd;
299 }
300
301 /* Convenience method used in testing. */
302 private SymbolData getArraySymbolData(String eltClassName, SourceInfo si, boolean addError, boolean checkImports) {
303 return _getArraySymbolData(eltClassName, si, addError, checkImports /*, _classesInThisFile*/);
304 }
305
306 /** Gets the SymbolData for an array type given className, the class name for its element type. First, it looks up the
307 * the SymbolData for className. If it's not null, then it tries to lookup the SymbolData for className + "[]"
308 * If it's found, return it. If not, create a new ArrayData and put it in the symbolTable. This method is a leaf
309 * in the 'get...Symbol' hierarchy.
310 * @param className The String name of the array type we're trying to resolve.
311 * @param si The SourceInfo corresponding to the class. Used if an error is encountered..
312 */
313 private SymbolData _getArraySymbolData(String eltClassName, SourceInfo si, boolean addError, boolean checkImports/*,
314 HashSet<String> classesInThisFile*/) {
315 // resolve should only be true when post-visitation resolution is performed
316 // if (eltClassName.equals("String[]")) System.err.println("String[] passed to getArraySymbolData");
317 // if (eltClassName.equals("String")) System.err.println("String passed to getArraySymbolData");
318 SymbolData eltSD = getSymbolData(eltClassName, si, addError, checkImports/*, classesInThisFile*/);
319 if (eltSD != null) {
320 // if (eltSD.getName().equals("java.lang.String")) System.err.println("java.lang.String FOUND");
321 SymbolData sd = symbolTable.get(eltSD.getName() + "[]"); // Look up fully qualified name
322 if (sd != null) return sd;
323 else return defineArraySymbolData(eltSD, this, si /*, classesInThisFile*/);
324 }
325 else return null;
326 }
327
328 /** Get the SymbolData for the array type given the fully qualified className for the element type. Lookup the
329 * element type; if it's not null, then try to lookup the symbol for the qualifiedClassName + "[]". If it's already
330 * there, return it. If not, create a new ArrayData and put it in the Symbol Table. This method is a leaf in the
331 * 'get...SymbolDataHelper' hierarchy.
332 * @param className The String name of the array type we're trying to resolve.
333 * @param si The SourceInfo corresponding to the class. Used if an error is encountered.
334 * @param resolve true if this SymbolData needs to be completely resolved.
335 * @param fromClassFile true if this type is from a class file.
336 */
337 private SymbolData _getQualifiedArraySymbolData(String eltClassName, SourceInfo si, boolean resolve,
338 boolean fromClassFile /*, HashSet<String> classesInThisFile*/) {
339 // resolve should only be true when post-visitation resolution is performed
340 SymbolData eltSD = getQualifiedSymbolData(eltClassName, si, resolve, fromClassFile, true /*, classesInThisFile*/);
341 if (eltSD != null) {
342 SymbolData sd = symbolTable.get(eltSD.getName() + "[]"); // Look up fully qualified name
343 if (sd != null) return sd;
344 else return defineArraySymbolData(eltSD, this, si /*, classesInThisFile*/);
345 }
346 else return null;
347 }
348
349 /** Checks the file system for the class name, returning the corresponding SymbolData if there is an up-to-date match.
350 * If resolve is false but a corresponding source file is matched, a continuation is returned. If no source file is
351 * found but a class file is found, the symbol is resolved against the class file even if resolve is false.
352 * If resolve is true and a corresponding source file is found, the symbol is resolved against the corresponding
353 * class file provided it is up-to-date. If it is not up-to-date, the method throws an error message. If no source
354 * file or class file corresponding to the class is found, SymbolData.NOT_FOUND is returned.
355 * @param qualifiedClassName The name of the class we're looking up.
356 * @param si Information about where the class was called from.
357 * @param resolve true if we want to fully resolve the SymbolData.
358 * @param addError true if we want to throw errors.
359 ` */
360 protected SymbolData _getSymbolDataFromFileSystem(final String qualifiedClassName, SourceInfo si, boolean resolve,
361 boolean addError) {
362 // If qualifiedClassName is already defined (and not a continuation to resolve), return
363 SymbolData sd = symbolTable.get(qualifiedClassName);
364 if (sd != null && (! sd.isContinuation() || ! resolve)) return sd;
365 // Note: sd != null && ! sd.isContinuation => sd has already been resolved
366
367 // // Is qualifiedClassName in _classesInThisFile, look it up directly in the parsed ASTs
368 // boolean present = _classesInThisFile.contains(qualifiedClassName);
369 // if (present) return _identifyTypeFromClassNamesInThisfile(qualifiedClassName, si, resolve);
370
371 /* If qualifiedClassName is not in the symbol table or list of classes to be parsed, check if the class is defined
372 * in this package tree.
373 */
374 String qualifiedClassNameWithSlashes =
375 qualifiedClassName.replace('.', System.getProperty("file.separator").charAt(0));
376 File _fileParent = _file.getParentFile();
377 // Create var that is eventually set to root of package (for _file)
378 String programRoot = (_fileParent == null) ? "" : _fileParent.getAbsolutePath();
379 assert (programRoot != null); // parent != null => parent exists.
380
381 final String path; // The expected path name of the class file (less .class) for qualifiedClassName
382
383 if (programRoot.length() > 0) {
384 String packageWithSlashes = _package.replace('.', System.getProperty("file.separator").charAt(0));
385 // Get index of slash preceding first char of package name
386 int indexOfPackage = programRoot.lastIndexOf(packageWithSlashes);
387 if (indexOfPackage < 0) path = qualifiedClassName;
388 else {
389 programRoot = programRoot.substring(0, indexOfPackage);
390 path = programRoot + System.getProperty("file.separator") + qualifiedClassNameWithSlashes;
391 }
392 }
393 else {
394 path = qualifiedClassNameWithSlashes; // Using file system root for programRoot
395 }
396
397 String dirPath; /* the expected directory of the file we're trying to resolve */
398 String newPackage = ""; /* the package of the file we're trying to resolve */
399 int lastSlashIndex = qualifiedClassNameWithSlashes.lastIndexOf(System.getProperty("file.separator"));
400 if (lastSlashIndex != -1) {
401 String newPackageWithSlashes = qualifiedClassNameWithSlashes.substring(0, lastSlashIndex);
402 dirPath = programRoot + System.getProperty("file.separator") + newPackageWithSlashes;
403 newPackage = newPackageWithSlashes.replace(System.getProperty("file.separator").charAt(0), '.');
404 }
405 else {
406 int lastPathSlashIndex = path.lastIndexOf(System.getProperty("file.separator"));
407 if (lastPathSlashIndex != -1) dirPath = path.substring(0, lastPathSlashIndex);
408 else dirPath = "";
409 }
410
411 // Find class file and matching source file for qualifiedClassName -- if they exist
412
413 File classFile = new File(path + ".class"); // create File object for class file
414
415 // Then look for the most recently modified matching source (.djx or .java) file.
416 File[] sourceFiles = new File(dirPath).listFiles(new FileFilter() {
417 public boolean accept(File f) {
418 try {
419 f = f.getCanonicalFile();
420 return new File(path + ".dj").getCanonicalFile().equals(f) ||
421 new File(path + ".dj0").getCanonicalFile().equals(f) ||
422 new File(path + ".dj1").getCanonicalFile().equals(f) ||
423 new File(path + ".dj2").getCanonicalFile().equals(f) ||
424 new File(path + ".java").getCanonicalFile().equals(f);
425 }
426 catch (IOException e) { return false; }
427 }});
428
429 File sourceFile = null; // sourceFile is either null or an existing file
430 if (sourceFiles != null) {
431 long mostRecentTime = 0;
432 for (File f : sourceFiles) {
433 long currentLastModified = f.lastModified();
434 if (f.exists() && mostRecentTime < currentLastModified) {
435 mostRecentTime = currentLastModified;
436 sourceFile = f;
437 }
438 }
439 }
440
441 // // Claim: sourceFile is not the current file or sd is an inner class. Otherwise, className would have been in
442 // // _classesInThisFile.
443
444 // If sourceFile exists, we have identified the class corresponding to qualifiedClassName. If resolve is false,
445 // simply create and return the appropriate continuation, deferring the loading of class information until reolution
446 // time. If there is no corresponding class file or the class file is not
447 // up-to-date, signal an error. Otherwise load the symbol table information from the class file
448
449 if (sourceFile != null) {
450 // First see if we even need to resolve this class. If not, create a continuation and return it.
451 if (! resolve) {
452 assert sd == null;
453 sd = makeContinuation(si, qualifiedClassName); // create a continuation for qualifiedClassName; defer resolution
454 return sd;
455 // else {
456 // sd = new SymbolData(qualifiedClassName);
457 // continuations.put(qualifiedClassName,
458 // new Triple<SourceInfo, LanguageLevelVisitor, SymbolData>(si, this, sd));
459 // symbolTable.put(qualifiedClassName, sd);
460 // return sd;
461 }
462 // Get last modified time of corresponding class file
463 long classModTime = classFile.lastModified();
464 if (classModTime == 0L) return null; // if classFile does not exist, return
465
466 if (sourceFile.lastModified() > classModTime) { // the class file is out of date
467 if (addError) {
468 _addAndIgnoreError("The file " + sourceFile.getAbsolutePath() +
469 " needs to be recompiled; it's class files either do not exist or are out of date.",
470 new NullLiteral(si));
471 }
472 return null;
473 }
474 }
475
476 // if source file exists, confirm that the corresponding class file is up to date
477 // if source file does not exist, confirm that a class file does exist
478 if (classFile.exists()) {
479 // read this classfile, create the SymbolData and return it
480 _log.log("Reading classFile " + qualifiedClassName);
481 sd = LanguageLevelConverter._classFile2SymbolData(qualifiedClassName, programRoot);
482 if (sd == null) {
483 if (addError) {
484 _addAndIgnoreError("File " + classFile + " is not a valid class file.", new NullLiteral(si));
485 }
486 return null;
487 }
488 _log.log("Returning symbol constructed by loading class file");
489 return sd;
490 }
491 return SymbolData.NOT_FOUND;
492 }
493
494 /** Resolves the continuation cont. */
495 public SymbolData resolveSymbol(SourceInfo si, SymbolData cont) {
496 // System.err.println("***ALARM*** resolveSymbol called for '" + cont + "'");
497 return getQualifiedSymbolData(cont.getName(), si, true);
498 }
499
500 /** Call getSymbolData with some default values. By default addError is true, since we want to
501 * display errors. By default checkImports is true, since we want to consider imported packages and classes.
502 * @param className The referenced name of the class to resolve.
503 * @param si The SourceInfo corresponding to the reference to the type
504 */
505 public SymbolData getSymbolData(String className, SourceInfo si) {
506 return getSymbolData(className, si, true, true);
507 }
508
509 /** Call getSymbolData with some default values. By default checkImports will be true, since we want to
510 * consider imported packages and classes initially.
511 * @param className The referenced name of the class to identify
512 * @param si The SourceInfo corresponding to the reference to the type
513 * @param addError true if we want to give an error if this class cannot be resolved.
514 */
515 protected SymbolData getSymbolData(String className, SourceInfo si, boolean addError) {
516 return this.getSymbolData(className, si, addError, true);
517 }
518
519 /** Simple signature for getSymbol that uses the current context to fill in context information, i.e., it passes _file
520 * for file, _package for pkg, _importedFiles for importedFiles, _importedPackages for importedPackages, and
521 * _enclosingClass for enclosingClassName. This version should be used in all contexts EXCEPT fixups which are
522 * executed outside of any context and must provide saved context information.
523 */
524 protected SymbolData getSymbolData(String className, SourceInfo si, boolean addError, boolean checkImports) {
525 return getSymbolData(_file, _package, _importedFiles, _importedPackages, _enclosingClassName,
526 className, si, addError, checkImports);
527 }
528
529 /** This method processes classNames which may or may not include qualifying prefixes. Array types are recognized and
530 * treated recursively. The raw className is initially compared with:
531 * * top-level classes defined in the this file;
532 * * fully qualified classes in the file system;
533 * * inner classes defined in the enclosing class;
534 * * classes in the same package defined in other files;
535 * * imported classes; and
536 * * classes in imported packages.
537 * Then className is decomposed in a prefix and an extension where prefixes are matched against symbols as described
538 * above. If a matching prefix is found, the remainder is matched against inner classes of the matched symbol.
539 * The protocol does not exactly match the one in the JLS.
540 * This results of method are relative to _file, _package, _importedFiles, _importedPackages, and _enclosingClassName.
541 * The external variables _classesInThisFile and _innerClassesInThisBody are used to reduce the number of fixups but
542 * should not affect the ultimate results (after fixups) of any searches.
543 * This method calls getQualifiedSymbolData to look up fully qualified class names in the symbol table; this process
544 * does not depend on anything but the contents of the symbol table.
545 * @param file The file containing the className reference.
546 * @param package The package corresponding to file.
547 * @param importedFiles The imported files for this file
548 * @param importedPackages The imported Packages for this file
549 * @param enclosingClassName The enclosing className
550 * @param className The referenced name of the class to lookup.
551 * @param si The SourceInfo of the reference to className used in case of an error.
552 * @param addError Whether to add errors or not
553 * @param checkImports Whether to try prepending the imported package names
554 */
555 protected SymbolData getSymbolData(File file,
556 String pkg,
557 LinkedList<String> importedFiles,
558 LinkedList<String> importedPackages,
559 String enclosingClassName,
560 String className,
561 SourceInfo si,
562 boolean addError,
563 boolean checkImports) {
564
565 if (className == null) {
566 // System.err.println("***ERROR*** getSymbolData called with null className");
567 assert false;
568 }
569
570 /** Check to see if type with className (as is) can be found (including a check against generic type variables). */
571 SymbolData existingSD = getQualifiedSymbolData(className, si, false, false, addError);
572 if (existingSD != null) return existingSD;
573
574 if (className.endsWith("[]")) { // className refers to an array type
575 String eltClassName = className.substring(0, className.length() - 2); // may not be fully qualified
576 // if (eltClassName.equals("String")) System.err.println("getSymbolData called for String[]");
577 return getArraySymbolData(/* file, pkg, importedFiles, importedPackages, enclosingClassName, */
578 eltClassName, si, addError, checkImports);
579 }
580
581 // Try matching the className against current package
582 String qualClassName = getQualifiedClassName(pkg, className); // TODO: make this work for an inner class
583 existingSD = getQualifiedSymbolData(qualClassName, si);
584 if (existingSD != null) return existingSD;
585
586 // Check for relative inner class reference
587 if (enclosingClassName != null) {
588 // Assume that className is an inner class relative to _enclosingClassName (which always holds for local
589 // classes and often holds for immediate inner class references. Fortunately, local class references cannot be
590 // forward references.
591 SymbolData enclosingSD = getQualifiedSymbolData(enclosingClassName, si);
592 if (enclosingSD != null) {
593 SymbolData sd = enclosingSD.getInnerClassOrInterface(className);
594 if (sd != null) return sd;
595 // // NOTE: the following should be unnecessary since the forward referenced inner symbol should be sd above
596 // // Check for forward reference to an inner class of the enclosing class
597 // String qualifiedName = enclosingSD + '.' + className;
598 // if (_innerClassesInThisBody.contains(qualifiedName)) // forward reference to inner class/interface
599 // return getQualifiedSymbolData(qualifiedName, si); // return continuation
600 }
601 }
602
603 // TODO: imported inner class can have qualification so the following logic is broken. The following logic
604 // ignores the possibility of importing an inner class. Fix this !!!
605 if (className.indexOf('.') == -1) { // className has no qualification; may be imported
606
607 // Check if the className's package was imported.
608 if (checkImports) {
609 // if (className.equals("Object")) System.err.println("***SHOUT*** checking imports for 'Object'");
610
611 // Check if className was specifically imported.
612 // We will not check that the package is correct here, because it is caught in the type checker.
613 Iterator<String> iter = importedFiles.iterator();
614 while (iter.hasNext()) {
615 String s = iter.next();
616 if (s.endsWith(className)) {
617 SymbolData importSD = symbolTable.get(s); // All imported files should be in the symbol table.
618 // if (importSD == null) System.err.println("***ALARM*** Imported symbol lookup failed for " + s);
619 // if importSD is a continuation it will be subsequently be resolved
620 return importSD;
621 }
622 }
623 }
624
625 // Look for a match against imported packages
626 // TODO: Within a relative class name the separators must be converted from '.' to '$'
627 SymbolData resultSD = null;
628 assert importedPackages.contains("java.lang");
629 // assert symbolTable.containsKey("java.lang.Object");
630 for (String prefix: importedPackages) {
631 String s = prefix + '.' + className;
632 // if (className.equals("java.lang.Object")) System.err.println("***ALARM*** Looking up: " + s);
633 SymbolData sD = getQualifiedSymbolData(s, si, false, false, false);
634 // if (qualClassName.equals("java.lang.Object"))
635 // System.err.println("matching sd is: " + sD + "\nsymbolTable.get(\"" + s + "\") = "+ symbolTable.get(s));
636 if (sD != null) {
637 if (resultSD == null || resultSD.equals(sD)) resultSD = sD;
638 else { // sD is NOT the first match; flag an error
639 if (addError) { // TODO: why do we suppress this error in some cases?
640 _addAndIgnoreError("The class name " + qualClassName + " is ambiguous. It could be " + resultSD.getName()
641 + " or " + sD.getName(), new NullLiteral(si));
642 return null;
643 }
644 }
645 }
646 }
647 if (resultSD != null) return resultSD;
648 else return null; // subsequent searching assumes that className is qualified.
649 }
650
651 // Decompose class name as fully qualified name followed by an inner class reference
652 // TODO: the separator within inner class names is '$'
653 int indexOfNextDot = 0;
654 // int indexOfNextDollar = className.indexOf("$"); // '$' only appears as separator for inner class names
655 SymbolData sd;
656 int length = className.length();
657 while (indexOfNextDot != length) {
658 indexOfNextDot = className.indexOf('.', indexOfNextDot + 1);
659 if (indexOfNextDot == -1) { indexOfNextDot = length; }
660 String prefix = className.substring(0, indexOfNextDot);
661
662 /* We want to try finding each prefix in the symbol table; the decomposition is putative. */
663 sd = getQualifiedSymbolData(prefix, si, false, false, false);
664 if (sd != null && sd != SymbolData.AMBIGUOUS_REFERENCE) { // prefix matches an existing symbol
665 String outerClassName = prefix;
666 String innerClassName = "";
667 if (indexOfNextDot != length) {
668 SymbolData outerClassSD = sd;
669 innerClassName = className.substring(indexOfNextDot + 1); // putative relative name of inner class
670 // System.err.println("Outer class prefix found: " + prefix + " inner class extension: " + innerClassName);
671 // NOTE: should be able to search symbolTable using getSymbolData
672 sd = outerClassSD.getInnerClassOrInterface(innerClassName);
673 if (sd != null) return sd;
674 // System.err.println("Corresponding symbol = " + sd);
675 // if (sd == null) { // create continuation for inner class; we are forbidding some ambiguities Java may permit
676 // sd = addInnerSymbolData(si, outerClassName + '.' + innerClassName, outerClassSD);
677 // }
678 /* otherwise try another decomposition. */
679 }
680 }
681 }
682 return null;
683 }
684
685 /** Try to look up name from the context of the lhs.
686 * @param lhs The TypeData corresponding to the enclosing of this name reference
687 * @param name The name piece to look up from the context of lhs
688 * @param si The SourceInfo corresponding to this reference
689 * @param addError true if an error should be added
690 * @return The SymbolData corresponding to this lookup, or NOT_FOUND or null if it could not be found
691 */
692 protected SymbolData getSymbolData(TypeData lhs, String name, SourceInfo si, boolean addError) {
693 //arguments we do not need to pass in
694 boolean checkImports = false;
695
696 if (lhs == null) return null;
697
698 else if (lhs instanceof PackageData) {
699 String qualClassName = lhs.getName() + '.' + name;
700 return getQualifiedSymbolData(qualClassName, si, false, false, addError);
701 }
702
703 else { // if (lhs instanceof SymbolData) {
704 SymbolData result = lhs.getInnerClassOrInterface(name);
705 if (result == SymbolData.AMBIGUOUS_REFERENCE) {
706 if (addError) { _addAndIgnoreError("Ambiguous reference to class or interface " + name, new NullLiteral(si)); }
707 return null;
708 }
709 return result;
710 }
711 }
712
713 /** Tries to find (or in some cases creates) the SymbolData for the fiven fully qualified class name. It
714 * searches imported files, primitive types, as well as types in the symbol table. */
715 protected SymbolData getQualifiedSymbolData(String qualClassName) {
716 return getQualifiedSymbolData(qualClassName, SourceInfo.NONE);
717 }
718
719 /** Tries to find (or in some cases creates) the SymbolData for the fiven fully qualified class name. It
720 * searches imported files, primitive types, as well as types in the symbol table. */
721 protected SymbolData getQualifiedSymbolData(String qualClassName, SourceInfo si) {
722 return getQualifiedSymbolData(qualClassName, si, false, false, true);
723 }
724
725 /** This method tries to find (or in some cases creates) the SymbolData for the fiven fully qualified class name. It
726 * searches imported files, primitive types, as well as types in the symbol table. If resolve is true, it loads the
727 * symbolTable with all of the requisite information about qualClassName. */
728 protected SymbolData getQualifiedSymbolData(String qualClassName, SourceInfo si, boolean resolve) {
729 return getQualifiedSymbolData(qualClassName, si, resolve, false, true);
730 }
731
732
733 /** This method tries to find (or in some cases creates) the SymbolData for the fiven fully qualified class name or
734 * class name. It uses _classesInThisFile to avoid returning null in some cases (eliminating the need for some
735 * fixups). Except for the _classesInThisFile optimization (which works uniformly if it is set to null during
736 * continuation resolution and fixups), this lookup only depends on the contents of the symbol table and the file
737 * system.
738 * @param qualClassName The fully qualified name of the class to lookup.
739 * @param si The SourceInfo of the reference to qualClassName used in case of an error.
740 * @param resolve Whether to return a continuation or fully parse the class.
741 * @param fromClassFile Whether this was called from the class file reader.
742 * @param addError Whether to add errors. We don't add errors when iterating through a qualified class name's
743 * package. (??)
744 */
745 protected SymbolData getQualifiedSymbolData(String qualClassName, SourceInfo si, boolean resolve, boolean fromClassFile,
746 boolean addError) {
747 assert qualClassName != null;
748 // if (qualClassName.startsWith("RefInnerClassCrazy"))
749 // System.err.println("ALARM: getQualifiedSymbolData called for '" + qualClassName + "'");
750 //
751 if (qualClassName.equals("java.lang.Throwable")) {
752 // System.err.println("***ALARM: getQualifiedSymbolData called for '" + qualClassName + "'");
753 // if (symbolTable.get(qualClassName) != null) System.err.println("***ALARM: java.lang.Throwable already exists");
754 }
755
756 assert (qualClassName != null && ! qualClassName.equals(""));
757
758 // Check for primitive types.
759 // System.err.println("***** Checking for primitive symbol " + qualClassName);
760 SymbolData sd = LanguageLevelConverter._getPrimitiveSymbolData(qualClassName);
761 if (sd != null) {
762 // System.err.println("***** Matched for primitive symbol " + sd);
763 return sd;
764 }
765
766 // Check for references to generic types (only happens in FullJava code)
767 // TODO !!! Does not handle forward references
768 String name = getUnqualifiedClassName(qualClassName);
769 if (_genericTypes.containsKey(name)) {
770 // Utilities.show("Return type " + name + " is generic and value is " + _genericTypes.get(name));
771 return _genericTypes.get(name);
772 }
773
774 // Check for already defined types
775 SymbolData existingSD = symbolTable.get(qualClassName);
776 if (existingSD != null && (! resolve || ! existingSD.isContinuation())) return existingSD;
777
778 // Check for array types.
779 if (qualClassName.endsWith("[]"))
780 return _getQualifiedArraySymbolData(qualClassName.substring(0, qualClassName.length() - 2), si, resolve,
781 fromClassFile);
782 // Check for generic type variables
783 SymbolData genericBinding = _genericTypes.get(qualClassName);
784 if (genericBinding != null) return genericBinding; // may return SymbolData.NOT_FOUND because bound is undefined
785
786 // If qualClassName is a library file, resolve it immediately by reading its class file.
787 if (isJavaLibraryClass(qualClassName)) {
788 _log.log("Calling _classFile2SymbolData");
789 SymbolData cfSD = LanguageLevelConverter._classFile2SymbolData(qualClassName, null);
790 if (! qualClassName.startsWith("java.") && ! qualClassName.startsWith("sun."))
791 // System.err.println("Defining class file symbol " + qualClassName);
792 assert cfSD == null || symbolTable.contains(cfSD);
793 return cfSD;
794 }
795
796 if (_classesInThisFile.contains(qualClassName)) // Make continuation for top level class not yet parsed in this file
797 return makeContinuation(si, qualClassName);
798
799 // If performing post-visit resolution, read the signature info for this symbol from a class file
800 if (resolve) { // Look for up-to-date class file
801 SymbolData newSd = _getSymbolDataFromFileSystem(qualClassName, si, true, true); // resolve, addError = true
802 if (newSd != null && newSd != SymbolData.NOT_FOUND) {
803 _log.log("Returning " + sd + " from file system");
804 return newSd;
805 }
806 else {
807 // System.err.println("********* ALARM: The class " + qualClassName + " was not found. Symbol = " + newSd);
808 _addAndIgnoreError("The class " + qualClassName + " was not found.", new NullLiteral(si));
809 assert false;
810 }
811 }
812 return null; // qualClassName not found
813 }
814
815
816 /** The Qualified Class Name is the package, followed by a dot, followed by the rest of the class name.
817 * If the provided className is already qualified, just return it. If the package is not empty,
818 * and the className does not start with the package, append the package name onto the className, and return it.
819 * @param className The className to qualify.
820 */
821 protected String getQualifiedClassName(String className) { return getQualifiedClassName(_package, className); }
822
823 /** If the specified package pkg is empty or pkg is a prefix of className, return className. Otherwise return
824 * className qualified with the pkg prefix.
825 * @param pkg The package name to use as a prefix.
826 * @param className The className to qualify.
827 */
828 public static String getQualifiedClassName(String pkg, String className) {
829 // if (className.equals("java")) throw new RuntimeException("BOGUS getQualifiedClassName call on 'java'");
830 if (! pkg.equals("") && ! className.startsWith(pkg)) return pkg + '.' + className;
831 else return className;
832 }
833
834 // Creates a continuation for an inner class/interface; qualifiedTypeName is known to exist
835 protected SymbolData addInnerSymbolData(SourceInfo si, String qualifiedTypeName, Data enclosing) {
836 SymbolData sd = makeContinuation(si, qualifiedTypeName); // create continuation
837 SymbolData enclosingSD = enclosing.getSymbolData(); // must exist in symbol table
838 // if qualifiedTypeName refers to an external inner class, the following will likely fail. TODO: eliminate this
839 enclosing.getSymbolData().addInnerClass(sd);
840 sd.setOuterData(enclosingSD);
841 return sd;
842 }
843
844 /** This method creates the specified continuation in the symbol table. Assumes qualClassName is fully qualified.
845 * @param si The SourceInfo corresponding to this occurrence of the class symbol
846 * @param referencedClassName The referenced name for the class. In some cases, it is fully qualified.
847 */
848 protected SymbolData makeContinuation(SourceInfo si, String qualClassName) {
849 // System.err.println("***** makeContinuation called for " + qualClassName);
850 if (qualClassName.equals("D.E")) assert false;
851 SymbolData sd = new SymbolData(qualClassName); // create a continuation
852 symbolTable.put(qualClassName, sd);
853 continuations.put(qualClassName, new Triple<SourceInfo, LanguageLevelVisitor, SymbolData>(si, this, sd));
854 // System.err.println("Created continuation for " + qualClassName + " at LLV:1124");
855 return sd;
856 }
857
858 /** Looks up the type with name rt (which is arbitrary source text for a type) from within the class name
859 * qualifiedClassName. At top level, qualifiedClassName == null. The parameter qualifiedClassName is required
860 * becaue this method may be called in a fixup. TODO: consolidate with _identifyType. */
861 protected SymbolData _lookupTypeFromWithinClass(ReferenceType rt, String qualifiedClassName) {
862 // Perform a raw lookup assuming name of rt is fully qualified
863 String rtName = rt.getName();
864 SourceInfo si = rt.getSourceInfo();
865 // Perform a lookup at top level.
866 assert _importedPackages.contains("java.lang");
867 SymbolData sD = getSymbolData(rtName, si, false);
868 // if (rtName.equals("Object") && sD == null)
869 // System.err.println("Looking up 'Object' in '" + qualifiedClassName + "' yields null");
870 if (sD == null && qualifiedClassName != null) { // check if rt refers to an inner type of qualifiedClassName
871 SymbolData sd = getQualifiedSymbolData(qualifiedClassName, SourceInfo.NONE);
872 sD = sd.getInnerClassOrInterface(rtName);
873 assert sD == getQualifiedSymbolData(qualifiedClassName + '.' + rtName, SourceInfo.NONE);
874 } // The following case should be unncessary because getInnerClassOrInterface should look back through enclosing classes
875 else if (qualifiedClassName != null) {
876 int prefixLen = qualifiedClassName.lastIndexOf('.');
877 if (sD == null && prefixLen >= 0) { // check if rt refers to an inner class of the class enclosing sd
878 // Check to see if this is an inner class referencing an inner interface
879 String qualifyingBase = qualifiedClassName.substring(0, qualifiedClassName.lastIndexOf('.'));
880 SymbolData outerSD = getQualifiedSymbolData(qualifyingBase, SourceInfo.NONE);
881 if (outerSD != null) {
882 sD = outerSD.getInnerClassOrInterface(rtName);
883 assert sD == getQualifiedSymbolData(qualifyingBase + '.' + rtName, si);
884 // TODO: expand this search to include interfaces defined in any of the enclosing classes.
885 }
886 }
887 }
888 return sD;
889 }
890
891 /** Overloaded signature for defineSymbolData. Passes _enclosingClassName for enclosingClassName and
892 * _classesInThisFile for classesInThisFile */
893 protected SymbolData defineSymbolData(TypeDefBase typeDefBase, final String qualifiedTypeName) {
894 return defineSymbolData(typeDefBase, qualifiedTypeName, _enclosingClassName /*, _classesInThisFile*/ );
895 }
896
897 /** Given a TypeDefBase (which is either a ClassDef or an InterfaceDef) and the corresponding qualifiedTypeName, this
898 * method generates a SymbolData, and adds the name and SymbolData pair to the symbol table. It checks that this
899 * class is not already in the symbol table. This error should never happen for an inner class or interface.
900 * Assumes that the defined class is top level. If used for inner class definition, the caller must be perform
901 * any special inner class initialization.
902 * @param typeDefBase The AST node for the class def, interface def, inner class def, or inner interface def.
903 * @param qualifiedTypeName The fully qualified name of the class or interface
904 * @return the defined SymbolData or null if the type has already been defined
905 */
906 protected SymbolData defineSymbolData(final TypeDefBase typeDefBase, final String qualifiedTypeName,
907 final String enclosingClassName /*, final HashSet<String> classesInThisFile*/) {
908 assert (typeDefBase instanceof InterfaceDef) || (typeDefBase instanceof ClassDef);
909 assert ! qualifiedTypeName.startsWith("null.");
910 String name = qualifiedTypeName; // may be an interface
911 SymbolData contSd = symbolTable.get(qualifiedTypeName);
912 // System.err.println("In defineSymbolData call for " + qualifiedTypeName + ", contSd = " + contSd);
913 if (contSd != null && ! contSd.isContinuation()) {
914 _addAndIgnoreError("The class or interface " + name + " has already been defined.", typeDefBase);
915 return null;
916 }
917 // If no continuation exists, create a SymbolData for this definition
918 final SymbolData sd = (contSd == null) ? new SymbolData(qualifiedTypeName) : contSd;
919 symbolTable.put(qualifiedTypeName, sd);
920
921 // // Save _enclosingClassName in a final var; may be null if called at the top level, e.g. defining a
922 // // top level class or interface.
923 // final String enclosingClassName = _enclosingClassName;
924
925 // Make this SymbolData as a non-continuation
926 sd.setIsContinuation(false);
927 //Set the package to be the current package
928 sd.setPackage(_package);
929 // Set the MAV and type parameters (the latter are not used currently)
930 sd.setMav(typeDefBase.getMav());
931 sd.setTypeParameters(typeDefBase.getTypeParameters());
932
933 // Create the LinkedList for the SymbolDatas of the interfaces
934 final ArrayList<SymbolData> interfaces = new ArrayList<SymbolData>();
935
936 // Get or create SymbolDatas for the interfaces
937 ReferenceType[] rts = typeDefBase.getInterfaces();
938 for (int i = 0; i < rts.length; i++) {
939 final ReferenceType rt = rts[i];
940 final String rtName = rt.getName();
941 boolean forwardRef = false;
942 SymbolData iD = _lookupTypeFromWithinClass(rt, enclosingClassName);
943 if (iD != null && ! iD.isContinuation() && ! iD.isInterface()) {
944 _addAndIgnoreError("The symbol " + rtName + " is not an interface", typeDefBase);
945 }
946 if (iD == null || iD.isContinuation()) { // create a dummy symbol pending fixUp TODO: is this necessary?
947 iD = new SymbolData(rtName);
948 forwardRef = true;
949 }
950
951 interfaces.add(iD);
952 if (forwardRef) {
953 // create a fixup for this interface slot
954 final int j = i;
955 Command fixUp = new Command() {
956 public void execute() {
957 SymbolData newID = _lookupTypeFromWithinClass(rt, enclosingClassName);
958 if (newID == null) _addAndIgnoreError("The symbol " + rtName + " is not defined", typeDefBase);
959 else if (! newID.isInterface())
960 _addAndIgnoreError("The symbol " + rtName + " is not an interface", typeDefBase);
961 interfaces.set(j, newID);
962 sd.addEnclosingData(newID);
963 }
964 };
965 fixUps.add(fixUp);
966 }
967 }
968
969 // Set the inferfaces; fixups will be done on the elements of the interface ArrayList, but this does not
970 // add the found interface to the enclosing data of sd.
971 sd.setInterfaces(interfaces);
972
973 // Create SymbolData variable for superclass
974 SymbolData superSD = null;
975
976 // Get or create the SymbolData for the superclass/interface; setInterface and setSuperClass
977
978 if (typeDefBase instanceof InterfaceDef) {
979 // set Object as the super class of this, so that it will know it implements Object's methods.
980 SymbolData objectSD = getSymbolData("java.lang.Object", typeDefBase.getSourceInfo(), false);
981 sd.setInterface(true);
982 sd.setSuperClass(objectSD);
983 }
984
985 else if (typeDefBase instanceof ClassDef) {
986 sd.setInterface(false);
987 ClassDef cd = (ClassDef) typeDefBase;
988 final ReferenceType rt = cd.getSuperclass();
989 superSD = _lookupTypeFromWithinClass(rt, enclosingClassName);
990
991 if (superSD != null) sd.setSuperClass(superSD);
992 else {
993 Command fixUp = new Command() {
994 public void execute() {
995 SymbolData newSuperSD = _lookupTypeFromWithinClass(rt, enclosingClassName);
996 if (newSuperSD == null)
997 _addAndIgnoreError("The class " + sd + " has an undefined superclass " + rt, typeDefBase);
998 else // TODO: Does not check that newSuperSD is not an interace
999 sd.setSuperClass(newSuperSD);
1000 }
1001 };
1002 fixUps.add(fixUp);
1003 }
1004 }
1005
1006
1007 // Remove symbol name from continuation table.
1008 _log.log("REMOVING continuation " + qualifiedTypeName);
1009 continuations.remove(qualifiedTypeName);
1010
1011 // Add sd to the list of classes defined in program text; used to generate constructors. TODO: What about Full Java?
1012 if (! sd.isInterface()) { LanguageLevelConverter._newSDs.put(sd, this); }
1013
1014 _classesInThisFile.remove(qualifiedTypeName); // a no-op if qualifiedClassName is an inner class
1015 return sd;
1016 }
1017
1018 /** Takes in a TypeDefBase (which is either an InnerClassDef or an InnerInterfaceDef) and creates a SymbolData for it,
1019 * either by converting a continuation to it or by creating a new symbol (if no continuationis present).
1020 * @param typeDefBase The AST node for the class def, interface def, inner class def, or inner interface def.
1021 * @param relName The relative (unqualified) name of the symbol
1022 * @param qualifiedTypeName The fully qualified name for the class; null if this definition is a duplicate
1023 * @param enclosing The enclosing SymbolData or MethodData (for a local class defined within a method).
1024 */
1025 protected SymbolData defineInnerSymbolData(TypeDefBase typeDefBase, String relName, String qualifiedTypeName,
1026 Data enclosing) {
1027 assert (enclosing instanceof SymbolData) || (enclosing instanceof MethodData);
1028 /* IMPORTANT: this is defineSymbolData for inner classes! */
1029
1030 SymbolData sd = defineSymbolData(typeDefBase, qualifiedTypeName /*, _classesInThisFile*/);
1031 // if (sd == null) System.err.println("defineSymbolData failed for " + qualifiedTypeName);
1032 assert sd != null;
1033 // Set fields of sd that are required for innerSymbols
1034
1035 sd.setOuterData(enclosing);
1036
1037 if (sd.isInterface()) {
1038 // assert enclosing instanceof SymbolData;
1039 // assert enclosing.getName().equals(_enclosingClassName):
1040 ((SymbolData) enclosing).addInnerInterface(sd);
1041 }
1042 else if (! enclosing.getName().equals(_enclosingClassName)) {
1043 // sd is a local class embedded in a method. We need to add sd to the innerclasses of _enclosingClassName
1044 // if (! (enclosing instanceof MethodData))
1045 // System.err.println("***** In defineInnerSymbolData, enclosing = " + enclosing
1046 // + " but _enclosingClassName = " + _enclosingClassName);
1047 assert enclosing instanceof MethodData;
1048 SymbolData enclosingClassSD = getQualifiedSymbolData(_enclosingClassName);
1049 assert enclosingClassSD != null;
1050 enclosingClassSD.addInnerClass(sd);
1051 enclosing.addInnerClass(sd); // adds innerClass to list for the enclosing MethodData
1052 }
1053 else {
1054 // sd is a non-local inner class
1055 assert enclosing.getName().equals(_enclosingClassName);
1056 enclosing.addInnerClass(sd);
1057 // _innerClassesInThisBody.remove(sd); // a no-op if _innerClassesInThisBody is empty
1058 }
1059 return sd;
1060 }
1061
1062 /** This method takes in an AnonymousClassInstantion, generates a SymbolData for it, and
1063 * adds the name and SymbolData pair to the symbol table.
1064 * @param AnonymousClassInstantiation The AST node for the anonymous class instantiation.
1065 * @param qualifiedTypeName The fully qualified name of the class
1066 */
1067 protected SymbolData defineAnonymousSymbolData(final AnonymousClassInstantiation anonInst,
1068 final String qualifiedAnonName,
1069 final String superName) {
1070 // Generated name cannot be in symbolTable
1071 // System.err.println("defineAnonymousSymbolData called for " + qualifiedAnonName + " extending " + superName);
1072
1073 final SourceInfo si = anonInst.getSourceInfo();
1074 // Create a SymbolData for this definition
1075 final SymbolData sd = new SymbolData(qualifiedAnonName);
1076 symbolTable.put(qualifiedAnonName, sd);
1077
1078 // Make this SymbolData as a non-continuation
1079 sd.setIsContinuation(false);
1080 //Set the package to be the current package
1081 sd.setPackage(_package);
1082
1083 // sd.setMav(anonInst.getMav()); // What is Mav for anonymous class?
1084 // sd.setTypeParameters(anonInst.getTypeParameters());
1085
1086 if (_enclosingClassName != null) {
1087 SymbolData enclosingSD = getQualifiedSymbolData(_enclosingClassName, SourceInfo.NONE);
1088 assert (enclosingSD != null);
1089 enclosingSD.addInnerClass(sd);
1090
1091 // if (enclosingSD.getName().equals("HasAnonymousInnerClass"))
1092 // System.err.println("****** The SymbolData for " + sd + " added to the inner classes of " + enclosingSD);
1093
1094 // Set fields of sd that are required for innerSymbols
1095 sd.setOuterData(enclosingSD);
1096
1097 }
1098
1099 SymbolData superSD = getSymbolData(superName, anonInst.getSourceInfo());
1100
1101 if (superSD != null) {
1102 if (superSD.isInterface()) {
1103 sd.setSuperClass(getQualifiedSymbolData("java.lang.Object", si));
1104 sd.setInterfaces(new ArrayList<SymbolData>(Arrays.asList(new SymbolData[] { superSD })));
1105 }
1106 else sd.setSuperClass(superSD); // By default sd.getInterfaces() == new ArrayList<SymbolData>()
1107 }
1108 else {
1109 // Create a fixup
1110 Command fixUp = new Command() {
1111 public void execute() {
1112 SymbolData superSD = getSymbolData(superName, si);
1113 if (superSD == null)
1114 _addAndIgnoreError("The class/interface " + superName + " was not found.", anonInst);
1115 else if (superSD.isInterface()) {
1116 sd.setSuperClass(getQualifiedSymbolData("java.lang.Object", si));
1117 sd.setInterfaces(new ArrayList<SymbolData>(Arrays.asList(new SymbolData[] { superSD })));
1118 }
1119 else sd.setSuperClass(superSD); // By default sd.getInterfaces() == new ArrayList<SymbolData>()
1120 }
1121 };
1122 fixUps.add(fixUp);
1123 }
1124 return sd;
1125 }
1126
1127 /** This method is factored out of formalParameters2VariableData so it can be overridden in FullJavaVisitor.
1128 * @return the formal parameter mav appropriate for the language level; "Functional level" is default. */
1129 protected String[] getFormalParameterMav(Data d) { return new String[] {"final"}; }
1130
1131 /** Convert the specified array of FormalParameters into an array of VariableDatas which is then returned.
1132 * All formal parameters are automatically made final.
1133 * @param fps The AST node for the parameter list
1134 * @param enclosing The SymbolData for the enclosing class (not method!)
1135 * NOTE: enclosing refers to the enclosing class rather than enclosing method because any new types
1136 * defined in the method are not visible in the parameter list.
1137 * TODO: At the advanced level, this may need to be overwritten?
1138 */
1139 protected VariableData[] formalParameters2VariableData(FormalParameter[] fps, SymbolData enclosing) {
1140 assert enclosing != null /* && (enclosing instanceof SymbolData || enclosing instanceof BlockData)*/;
1141 // BodyData ::= MethodData | BlockData
1142
1143 // Utilities.show("formalParameters2VariableData called on " + fps);
1144 // Should consolidate with same method in FullJavadVisitor; almost identical
1145 final VariableData[] varData = new VariableData[fps.length];
1146 final String enclosingClassName = enclosing.getName();
1147
1148 String[] mav = getFormalParameterMav(enclosing);
1149
1150 for (int i = 0; i < fps.length; i++) {
1151 VariableDeclarator vd = fps[i].getDeclarator();
1152 String name = vd.getName().getText(); // getName returns a Word
1153
1154 Type type = vd.getType();
1155 final String typeName = type.getName();
1156 // if (name.equals("myArray"))
1157 // System.err.println("*** 2Var called for var " + name + " type = " + typeName);
1158 final SourceInfo si = type.getSourceInfo();
1159 // Note: typeName CANNOT be a local type; no such type is in scope
1160 SymbolData sd = _identifyType(typeName, si, enclosingClassName);
1161
1162 varData[i] =
1163 new VariableData(name, new ModifiersAndVisibility(SourceInfo.NONE, mav), sd, true, enclosing);
1164
1165 assert ! varData[i].isPrivate();
1166
1167 if (sd == null || sd == SymbolData.NOT_FOUND) { // a forward Type reference
1168 // To establish a reference to a not-yet-defined type, create a fixup
1169 final int j = i;
1170 /* The following is a kludge to make method signature collision detection work (unless different
1171 * names are used for the same type). */
1172 varData[j].setType(new SymbolData(typeName));
1173 Command fixUp = new Command() {
1174 public void execute() {
1175 SymbolData newSd = _identifyType(typeName, si, enclosingClassName);
1176 if (newSd == null || newSd == SymbolData.NOT_FOUND)
1177 System.err.println("****** In fixUp, the type " + typeName + " at " + si + " was NOT found.");
1178 // assert newSd != null && newSd != SymbolData.NOT_FOUND; // TODO !!!: Expand to error message?
1179 if (newSd != null) varData[j].setType(newSd);
1180 }
1181 };
1182 fixUps.add(fixUp);
1183 }
1184
1185 // System.err.println("For inner class/interface " + typeName + " found type " + type);
1186 varData[i].gotValue();
1187 varData[i].setIsLocalVariable(true);
1188 }
1189 return varData;
1190 }
1191
1192 // /** Identifies the SymbolData in symbolTable matching typeName. Returns null if no match is
1193 // * found. Searches for typeName as a fully qualified Name and as a relative name within
1194 // * the current class. TODO: match inner classes of classes enclosing the current class. */
1195 // private SymbolData _identifyVarType(String typeName, SourceInfo si) {
1196 // SymbolData sd = getSymbolData(typeName, si);
1197 // if (sd != null) return sd;
1198 // SymbolData enclosingSD = getQualifiedSymbolData(_enclosingClassName, SourceInfo.NONE);
1199 // assert enclosingSD != null;
1200 // return _enclosingClass.getInnerClassOrInterface(typeName);
1201 // // TODO: fails for nested inner classes
1202 // }
1203
1204 /** Looks up the return type of a method. */
1205 private SymbolData _lookupReturnString(String rtString, SourceInfo si) {
1206 return rtString.equals("void") ? SymbolData.VOID_TYPE : getSymbolData(rtString, si);
1207 }
1208
1209 /** Creates a MethodData corresponding to the MethodDef within the context of the SymbolData sd. */
1210 protected MethodData createMethodData(final MethodDef that, final SymbolData sd) {
1211
1212 assert _enclosingClassName != null && getQualifiedSymbolData(_enclosingClassName).equals(sd);
1213
1214 // _log.log("createMethodData(" + that + ", " + sd + ") called.");
1215 // System.err.println("createMethodData(" + that.getName().getText() + ", " + sd + ") called.");
1216 // System.err.println("_enclosingClassName = " + _enclosingClassName);
1217 that.getMav().visit(this);
1218 that.getName().visit(this);
1219
1220 // Turn the thrown exceptions from a ReferenceType[] to a String[]
1221 String[] throwStrings = referenceType2String(that.getThrows());
1222
1223 final String rtString = that.getResult().getName();
1224
1225 // Identify the return type
1226 final SourceInfo si = that.getResult().getSourceInfo();
1227 // if (! sd.equals(getQualifiedSymbolData(_enclosingClassName, SourceInfo.NONE))) {
1228 // System.err.println("sd = " + sd);
1229 // System.err.println("other = " + getQualifiedSymbolData(_enclosingClassName, SourceInfo.NONE));
1230 // assert false;
1231 // }
1232 // Note: rtString cannot be a local type; no such type is in scope. BUT it can be a type variable or
1233 // a forward reference.
1234 SymbolData returnType = _identifyType(rtString, si, _enclosingClassName);
1235
1236 final String name = that.getName().getText();
1237 // System.err.println("Creating MethodData for " + name + " in type " + sd);
1238 final MethodData md =
1239 MethodData.make(name, that.getMav(), that.getTypeParams(), returnType, null, throwStrings, sd, that);
1240 VariableData[] vds = formalParameters2VariableData(that.getParams(), sd);
1241
1242 if (returnType == null) {
1243 // System.err.println("Creating return type fixup for " + rtString + " in method " + name + " in class " + sd);
1244 final String enclosingClassName = _enclosingClassName;
1245 Command fixUp = new Command() {
1246 public void execute() {
1247 SymbolData newReturnType = _identifyType(rtString, si, enclosingClassName);
1248 if (newReturnType == null && (! (LanguageLevelVisitor.this instanceof FullJavaVisitor))) {
1249 _addAndIgnoreError("The return type " + rtString + " for method " + name + " in type " + sd + " is undefined.",
1250 that);
1251 // System.err.println("The return type " + rtString + " for method " + name + " in type " + sd + " is undefined.");
1252 // assert false;
1253 }
1254 else {
1255 // System.err.println("FixUp set the returnType of " + name + "in type " + sd + " to " + newReturnType);
1256 md.setReturnType(newReturnType);
1257 }
1258 }
1259 };
1260 fixUps.add(fixUp);
1261 }
1262
1263 // System.err.println("Called createMethodData(" + name + ", " + sd.getName() + ")");
1264 // _log.log("createMethodData called. Created MethodData " + md + '\n' + "with modifiers:" + md.getMav());
1265 // Turn the parameters from a FormalParameterList to a VariableData[]
1266
1267
1268 if (_checkError()) { //if there was an error converting the formalParameters, don't use them.
1269 return md;
1270 }
1271
1272 md.setParams(vds);
1273
1274 // Adds the formal parameters to the list of vars defined in this method.
1275 if (! md.addVars(vds)) { //TODO: should this not have been changed from addFinalVars?
1276 _addAndIgnoreError("You cannot have two method parameters with the same name", that);
1277 }
1278 return md;
1279 }
1280
1281 /** Generates a brief print string for a VariableDeclarator. */
1282 private static String declaratorsToString(VariableDeclarator[] vds) {
1283 StringBuilder printString = new StringBuilder("{ ");
1284 for (VariableDeclarator vd: vds) {
1285 printString.append(vd.getName().getText()).append(": ").append(vd.getType().getName()).append("; ");
1286 }
1287 return printString.append('}').toString();
1288 }
1289
1290 /** This method assumes that the modifiers for this particular VariableDeclaration have already been checked. It
1291 * does no semantic checking. It simply converts the declarators to variable datas, by trying to resolve the types
1292 * of each declarator. The VariableDeclaration may be a field declaration! The Data enclosing may be a MethodData!
1293 */
1294 protected VariableData[] _variableDeclaration2VariableData(VariableDeclaration vd, final Data enclosing) {
1295 assert enclosing != null;
1296 // System.err.println("*** 2Var called for \n" + declaratorsToString(vd.getDeclarators()) + "\nin " + enclosing);
1297 LinkedList<VariableData> vds = new LinkedList<VariableData>();
1298 ModifiersAndVisibility mav = vd.getMav();
1299 VariableDeclarator[] declarators = vd.getDeclarators();
1300 for (final VariableDeclarator declarator: declarators) {
1301 declarator.visit(this); // Does NOTHING!
1302 final Type type = declarator.getType();
1303 final String name = declarator.getName().getText();
1304 final String typeName = type.getName();
1305 // assert enclosing == getQualifiedSymbolData(_enclosingClassName, SourceInfo.NONE);
1306 // TODO: if enclosing is a MethodData, we should first look for a local class!!! This search will always
1307 // succeed if a matching local class exists because no forward reference is possible. (Confirm this!) !!!
1308 /* TODO: do we need to worry about case when enclosing is a MethodData? Yes. defineInnerSymbolData
1309 * already treats local classes specially, but it doesn't help. References to local types use relative
1310 * class names. */
1311 SymbolData sd = _identifyType(typeName, declarator.getSourceInfo(), _enclosingClassName); // may be null
1312 boolean initialized = declarator instanceof InitializedVariableDeclarator;
1313 // want hasBeenAssigned to be true if this variable declaration is initialized, and false otherwise.
1314 // System.err.println("Creating new VariableData " + name + " : " + typeName + " within " + _enclosingClassName);
1315 final VariableData vdata = new VariableData(name, mav, sd, initialized, enclosing);
1316 vdata.setHasInitializer(initialized);
1317 // vdata.setIsLocalVariable(true);
1318 vds.addLast(vdata);
1319 // System.err.println("identifyReturnType(" + type + ", " + name + ", " + enclosing + ") returned null");
1320 if (sd == null) { // TODO !!! Can this really happen?
1321 // Create fixup
1322 final String enclosingName = _enclosingClassName; // Grab the current enclosing class name
1323 // System.err.println("**** Creating fixup for preceding VariableData");
1324 Command fixup = new Command() {
1325 public void execute() {
1326 // System.err.println("**** Executing fixup for " + typeName + " within " + enclosingName);
1327 SymbolData newSd = _identifyType(typeName, declarator.getSourceInfo(), enclosingName);
1328 if (newSd != null) vdata.setType(newSd);
1329 else if (! (LanguageLevelVisitor.this instanceof FullJavaVisitor) ) // TODO: fix this kludge!!!
1330 _addAndIgnoreError("Class or Interface " + typeName + " not found", type);
1331 }
1332 };
1333 fixUps.add(fixup);
1334 }
1335 }
1336 // System.err.println("Returning VariableDatas " + vds);
1337 return vds.toArray(new VariableData[vds.size()]);
1338 }
1339
1340 /** Identifies the SymbolData matching name in symbolTable. Returns null if no match is found. Searches for typeName
1341 * as a fully qualified Name and as a relative name within the enclosing class. enclosingClassName is null if type
1342 * is part of the header for a class or interface. Methods can introduce local types. Make sure that we can match
1343 * inner classes of the chain of enclosing datas. We need to use the relative inner class name to do this. */
1344 protected SymbolData _identifyType(String name, SourceInfo si, String enclosingClassName) {
1345 // System.err.println("***** Calling _identifyType(" + name + ") within " + enclosingClassName);
1346
1347 // If name is a type variable, return the binding
1348 if (_genericTypes.containsKey(name)) return _genericTypes.get(name);
1349
1350 SymbolData sd = getSymbolData(name, si); // TODO: uses wrong enclosingClassName!!!
1351 if (sd != null) return sd;
1352
1353 // if (name.equals("ResType") && sd == null)
1354 // Utilities.show("_genericTypes = " + _genericTypes + "Trace follows.\n" + Utilities.getStackTrace());
1355
1356 // if (enclosingClassName == null) Utilities.show("_identifyType called with null enclosingClassName. Trace follows.\n" +
1357 // Utilities.getStackTrace());
1358
1359 // System.err.println("***ERROR*** in _identifyType " + enclosingClassName + " NOT FOUND");
1360 if (enclosingClassName == null) return null; // happens for binding occurrences of type variables?
1361
1362 SymbolData enclosingSD = getQualifiedSymbolData(enclosingClassName, SourceInfo.NONE);
1363 if (enclosingSD == null) return null;
1364
1365 sd = enclosingSD.getInnerClassOrInterface(name);
1366
1367 // // Create continuation for new type
1368 // String qualifiedTypeName = enclosingClassName + '.' + name;
1369 // if (_innerClassesInThisBody.contains(qualifiedTypeName)) {
1370 // // reference to an inner class that will subsequently be defined
1371 // sd = addInnerSymbolData(si, qualifiedTypeName, enclosingSD);
1372 // }
1373
1374 // System.err.println("***** _identifyType(" + name + ") within " + enclosingClassName + " RETURNED " + sd);
1375 return sd; // Note: sd is null if name is not identified.
1376 }
1377
1378 /** This method is called when an error should be added to the static LinkedList of errors.
1379 * This version is called from the DoFirst methods in the LanguageLevelVisitors to halt
1380 * parsing of the construct.
1381 */
1382 protected static void _addError(String message, JExpressionIF that) {
1383 // Utilities.show("_addError(" + message + ", " + that + ") called");
1384 _errorAdded = true;
1385 Pair<String, JExpressionIF> p = new Pair<String, JExpressionIF>(message, that);
1386 if (! errors.contains(p)) errors.addLast(p);
1387 }
1388
1389 /** This method is called when an error should be added, but tree-walking should continue
1390 * on this construct. Generally, if the error is not added in the DoFirst, the _errorAdded
1391 * flag is not checked anyway, so this version should be called.
1392 */
1393 protected static void _addAndIgnoreError(String message, JExpressionIF that) {
1394 // Utilities.show("_addAndIgnoreError(" + message + ", " + that + ") called");
1395 if (_errorAdded) {
1396 throw new RuntimeException("Internal Program Error: _addAndIgnoreError called while _errorAdded was true." +
1397 " Please report this bug.");
1398 }
1399 _errorAdded = false;
1400 Pair<String, JExpressionIF> newMsg = new Pair<String, JExpressionIF>(message, that);
1401 if (! errors.contains(newMsg)) errors.addLast(newMsg);
1402 // else System.err.println("Suppressing error as duplicate: " + newMsg);
1403 }
1404
1405 protected boolean prune(JExpressionIF node) { return _checkError(); }
1406
1407 /** If _errorAdded is true, set it back to false and return true.
1408 * This will cause the current construct to be skipped, but will allow this first pass
1409 * to otherwise continue unimpeded.
1410 * Otherwise, return false, which will allow this first pass to continue normally.
1411 */
1412 protected static boolean _checkError() {
1413 if (_errorAdded) {
1414 _errorAdded = false;
1415 return true;
1416 }
1417 else return false;
1418 }
1419
1420 /** Add an error explaining the modifiers' conflict. */
1421 public void _badModifiers(String first, String second, JExpressionIF that) {
1422 _addError("Illegal combination of modifiers. Can't use " + first + " and " + second + " together.", that);
1423 }
1424
1425 /** Check for problems with modifiers that are common to all language levels: duplicate modifiers and illegal
1426 * combinations of modifiers.
1427 */
1428 public Void forModifiersAndVisibilityDoFirst(ModifiersAndVisibility that) {
1429 String[] modifiersAndVisibility = that.getModifiers();
1430 Arrays.sort(modifiersAndVisibility);
1431 if (modifiersAndVisibility.length > 0) {
1432 String s = modifiersAndVisibility[0];
1433 // check for duplicate modifiers
1434 for (int i = 1; i < modifiersAndVisibility.length; i++) {
1435 if (s.equals(modifiersAndVisibility[i])) {
1436 _addError("Duplicate modifier: " + s, that);
1437 }
1438 s = modifiersAndVisibility[i];
1439 }
1440
1441 // check for illegal combination of modifiers
1442 String visibility = "package";
1443 boolean isAbstract = false;
1444 boolean isStatic = false;
1445 boolean isFinal = false;
1446 boolean isSynchronized = false;
1447 boolean isStrictfp = false;
1448 boolean isTransient = false;
1449 boolean isVolatile = false;
1450 boolean isNative = false;
1451 for (int i = 0; i < modifiersAndVisibility.length; i++) {
1452 s = modifiersAndVisibility[i];
1453 if (s.equals("public") || s.equals("protected") || s.equals("private")) {
1454 if (! visibility.equals("package")) _badModifiers(visibility, s, that);
1455 else if (s.equals("private") && isAbstract) _badModifiers("private", "abstract", that);
1456 else visibility = s;
1457 }
1458 else if (s.equals("abstract")) isAbstract = true;
1459 else if (s.equals("final")) {
1460 isFinal = true;
1461 if (isAbstract) _badModifiers("final", "abstract", that);
1462 }
1463 else if (s.equals("native")) {
1464 isNative = true;
1465 if (isAbstract) _badModifiers("native", "abstract", that);
1466 }
1467 else if (s.equals("synchronized")) {
1468 isSynchronized = true;
1469 if (isAbstract) _badModifiers("synchronized", "abstract", that);
1470 }
1471 else if (s.equals("volatile")) {
1472 isVolatile = true;
1473 if (isFinal) _badModifiers("final", "volatile", that);
1474 }
1475 }
1476 return forJExpressionDoFirst(that); // Does nothing!
1477 }
1478 return null;
1479 }
1480
1481 /** Check for problems in ClassDefs. Make sure that the top level class is
1482 * not private, and that the class name has not already been imported.
1483 */
1484 public Void forClassDefDoFirst(ClassDef that) {
1485 String name = that.getName().getText(); // name of defined class
1486 Iterator<String> iter = _importedFiles.iterator();
1487 while (iter.hasNext()) {
1488 String s = iter.next();
1489 if (s.endsWith(name) && ! s.equals(getQualifiedClassName(name))) { // TODO: this test is too coarse!
1490 _addAndIgnoreError("The class " + name + " was already imported.", that);
1491 }
1492 }
1493
1494 // top level classes cannot be private.
1495 String[] mavStrings = that.getMav().getModifiers();
1496 if (! (that instanceof InnerClassDef)) {
1497 for (int i = 0; i < mavStrings.length; i++) {
1498 if (mavStrings[i].equals("private")) {
1499 _addAndIgnoreError("Top level classes cannot be private", that);
1500 }
1501 }
1502 }
1503
1504 // See if this is a Blacklisted class. Blacklisted classes are any classes in java.lang or TestCase.
1505 SymbolData javaLangClass =
1506 getQualifiedSymbolData("java.lang." + that.getName().getText(), that.getSourceInfo(), false, false, false);
1507 if (that.getName().getText().equals("TestCase") || (javaLangClass != null && ! javaLangClass.isContinuation())) {
1508 _addError("You cannot define a class with the name " + that.getName().getText() +
1509 " because that class name is reserved." +
1510 " Please choose a different name for this class", that);
1511 }
1512 return forTypeDefBaseDoFirst(that);
1513 }
1514
1515 /** Check for problems with InterfaceDefs: specifically, top level interfaces cannot be private or final. */
1516 public Void forInterfaceDefDoFirst(InterfaceDef that) {
1517 //top level interfaces cannot be private or final.
1518 String[] mavStrings = that.getMav().getModifiers();
1519 for (int i = 0; i < mavStrings.length; i++) {
1520 if (mavStrings[i].equals("private")) {
1521 _addAndIgnoreError("Top level interfaces cannot be private", that);
1522 }
1523 if (mavStrings[i].equals("final")) {
1524 _addAndIgnoreError("Interfaces cannot be final", that);
1525 }
1526 }
1527 return forTypeDefBaseDoFirst(that);
1528 }
1529
1530 /** Check for problems with InnerInterfaceDefs that are common to all language levels: specifically, they cannot be
1531 * final.
1532 */
1533 public Void forInnerInterfaceDefDoFirst(InnerInterfaceDef that) {
1534 String[] mavStrings = that.getMav().getModifiers();
1535 for (int i = 0; i < mavStrings.length; i++) {
1536 if (mavStrings[i].equals("final")) {
1537 _addAndIgnoreError("Interfaces cannot be final", that);
1538 }
1539 }
1540 return forTypeDefBaseDoFirst(that);
1541 }
1542
1543 /** Do the common work for SimpleAnonymousClassInstantiations and ComplexAnonymousClassInstantiations
1544 * and in FullJava and Functional Java.
1545 * @param that The AnonymousClassInstantiation being visited.
1546 * @param enclosing The SymbolData of the enclosing class.
1547 * @param superC The super class being instantiated--i.e. new A() { ...}, would have a super class of A.
1548 */
1549 public void anonymousClassInstantiationHelper(AnonymousClassInstantiation that, SymbolData enclosing, String superName) {
1550 that.getArguments().visit(this);
1551 SymbolData enclosingSD = enclosing.getSymbolData();
1552 String enclosingSDName = enclosingSD.getName();
1553 assert enclosingSDName.equals(_enclosingClassName);
1554 String anonName = getQualifiedClassName(enclosingSDName) + "$" + enclosingSD.preincrementAnonymousInnerClassNum();
1555
1556 // System.err.println("****** In anonymousCIH the anonName = " + anonName + " superName = " + superName
1557 // + " enclosing = " + enclosing);
1558
1559 // Define the SymbolData that will correspond to this anonymous class
1560 SymbolData anonSD = defineAnonymousSymbolData(that, anonName, superName);
1561
1562 // if (this instanceof IntermediateVisitor) {
1563 // These methods are no-ops in FullJavaVisitor
1564 createToString(anonSD);
1565 createHashCode(anonSD);
1566 createEquals(anonSD);
1567 // Accessors will be filled in in typeChecker pass
1568 // }
1569
1570 // Visit the body (with the appropritate class body visitor to get it all nice and resolved.
1571 // System.err.println("Calling appropriate class body visitor for " + anonName);
1572 that.getBody().visit(newClassBodyVisitor(anonSD, anonName));
1573 }
1574
1575 /** Processes the class body that. */
1576 protected void identifyInnerClasses(TypeDefBase that) {
1577 String prefix = _enclosingClassName == null ? "" : _enclosingClassName + '.';
1578 String enclosingType = getQualifiedClassName(prefix + that.getName().getText());
1579 // System.err.println("***** identifyInnerClasses called for " + enclosingType + " in file " + _file);
1580 assert enclosingType != null;
1581 // Process the members of this class
1582 // System.err.println("Finding inner classes in " + enclosingType);
1583 SymbolData sd = getSymbolData(enclosingType, SourceInfo.NONE);
1584 // System.err.println("SymbolData for " + enclosingType + " is " + sd);
1585 enclosingType = sd.getName(); // that may be a local class, which has a more elaborate name
1586 BracedBody body = that.getBody();
1587 for (BodyItemI bi: body.getStatements()) {
1588 if (bi instanceof TypeDefBase) {
1589 TypeDefBase type = (TypeDefBase) bi;
1590 String rawClassName = type.getName().getText();
1591 // System.err.println("Adding " + rawClassName + " to inner classes of " + enclosingType + "\n");
1592 String fullClassName = enclosingType + '.' + rawClassName;
1593 // System.err.println("Adding " + rawClassName + " to _innerClassesInThisBody inside " + that + "\n");
1594
1595 // _innerClassesInThisBody.add(fullClassName);
1596 // System.err.println("***** Making continuation for " + fullClassName + " in file " + _file);
1597 SymbolData innerSD = makeContinuation(bi.getSourceInfo(), fullClassName);
1598 // System.err.println("***** Continuation " + innerSD + " returned");
1599 // Utilities.show("***** Continuation " + innerSD + " returned");
1600 sd.addInnerClass(innerSD);
1601 }
1602 }
1603 // System.err.println("_innerClassesInThisBody = " + _innerClassesInThisBody);
1604 }
1605
1606 /** Process the members/statements of the class body, method body, or ordinary body (e.g. a try body, catch clause
1607 * body, or compound statement body. This is VERY BAD data design. These various bodies have significantly
1608 * different meanings. */
1609 // public Void forBracedBodyDoFirst(BracedBody that) {
1610 // if (! (this instanceof BodyBodyFullJavaVisitor)) { // full Java method bodies are excluded by this test
1611 // // Process the members of this class
1612 // SymbolData sd = getSymbolData(_enclosingClassName, SourceInfo.NONE);
1613 // for (BodyItemI bi: that.getStatements()) {
1614 // if (bi instanceof TypeDefBase) {
1615 // TypeDefBase type = (TypeDefBase) bi;
1616 // String rawClassName = type.getName().getText();
1617 // _log.log("Adding " + rawClassName + " to _innerClassesInThisBody inside " + that + "\n");
1618 // String fullClassName = _enclosingClassName + '.' + rawClassName;
1619 //// System.err.println("Adding " + rawClassName + " to _innerClassesInThisBody inside " + that + "\n");
1620 //
1621 // _innerClassesInThisBody.add(fullClassName);
1622 // SymbolData innerSD = makeContinuation(bi.getSourceInfo(), fullClassName);
1623 // sd.addInnerClass(innerSD);
1624 // }
1625 // }
1626 // System.err.println("_innerClassesInThisBody = " + _innerClassesInThisBody);
1627 // }
1628 //
1629 // return super.forBracedBodyDoFirst(that);
1630 // }
1631
1632 /** This sets the package name field in order to find other classes in the same package. */
1633 public Void forPackageStatementOnly(PackageStatement that) {
1634 CompoundWord cWord = that.getCWord();
1635 Word[] words = cWord.getWords();
1636 String newPackage;
1637 String separator = System.getProperty("file.separator");
1638 if (words.length > 0) {
1639 _package = words[0].getText();
1640 newPackage = _package;
1641 for (int i = 1; i < words.length; i++) {
1642 String temp = words[i].getText();
1643 newPackage = newPackage + separator + temp;
1644 _package = _package + '.' + temp;
1645 }
1646 String directory = _file.getParent();
1647 if (directory == null || !directory.endsWith(newPackage)) {
1648 _addAndIgnoreError("The package name must mirror your file's directory.", that);
1649 }
1650 }
1651 // Call getSymbolData to see if this is actually a class as well as a Package Name. If it is, an error will be
1652 // given in the TypeChecking step.
1653 // If file is a .java file and not compiled, won't find it. This is not consistent with the JLS.
1654 // if file is a ll file and not compiled, will find it, though this is not consistent with the JLS.
1655 // getSymbolData(_package, that.getSourceInfo(), false);
1656 return forJExpressionOnly(that);
1657 }
1658
1659
1660 /** Make sure the class being imported has not already been imported.
1661 * If there are no errors, add it to the list of imported files, and create a continuation for it.
1662 * The class will be resolved later.
1663 */
1664 public Void forClassImportStatementOnly(ClassImportStatement that) {
1665 CompoundWord cWord = that.getCWord();
1666 Word[] words = cWord.getWords();
1667
1668 // Make sure that this specific imported class has not already been specifically imported
1669 for (int i = 0; i < _importedFiles.size(); i++) {
1670 String name = _importedFiles.get(i);
1671 int indexOfLastDot = name.lastIndexOf('.');
1672 if (indexOfLastDot != -1 &&
1673 (words[words.length-1].getText()).equals(name.substring(indexOfLastDot + 1, name.length()))) {
1674 _addAndIgnoreError("The class " + words[words.length-1].getText() + " has already been imported.", that);
1675 return null;
1676 }
1677 }
1678
1679 StringBuilder nameBuff = new StringBuilder(words[0].getText());
1680 for (int i = 1; i < words.length; i++) {nameBuff.append('.' + words[i].getText());}
1681
1682 String qualifiedTypeName = nameBuff.toString();
1683
1684 // // Make sure that this imported class does not duplicate the package. WHY? FIX THIS.
1685 // // Although this is allowed in full java, we decided to not allow it at any LanguageLevel.
1686 // int indexOfLastDot = qualifiedTypeName.lastIndexOf('.');
1687 // if (indexOfLastDot != -1) {
1688 // if (_package.equals(qualifiedTypeName.substring(0, indexOfLastDot))) {
1689 // _addAndIgnoreError("You do not need to import " + qualifiedTypeName
1690 // + ". It is in your package so it is already visible",
1691 // that);
1692 // return null;
1693 // }
1694 // }
1695
1696 //Now add the class to the list of imported files
1697 _importedFiles.addLast(qualifiedTypeName);
1698
1699 // Create a continuation for imported class if one does not already exist
1700 createImportedSymbolContinuation(qualifiedTypeName, that.getSourceInfo());
1701 return forImportStatementOnly(that);
1702 }
1703
1704 /** Create a continuation for imported class specified by qualifiedName if one does not already exist. */
1705 protected SymbolData createImportedSymbolContinuation(String qualifiedTypeName, SourceInfo si) {
1706
1707 SymbolData sd = symbolTable.get(qualifiedTypeName);
1708 if (sd == null) {
1709 // Create a continuation for the imported class and put it into the symbol table so
1710 // that on lookup, we can check imported classes before classes in the same package.
1711 // System.err.println("Creating continuation for imported class " + temp);
1712 sd = makeContinuation(si, qualifiedTypeName);
1713 }
1714 return sd;
1715 }
1716
1717 /**Check to make sure that this package import statement is not trying to import the current pacakge. */
1718 public Void forPackageImportStatementOnly(PackageImportStatement that) {
1719 CompoundWord cWord = that.getCWord();
1720 Word[] words = cWord.getWords();
1721 StringBuilder tempBuff = new StringBuilder(words[0].getText());
1722 for (int i = 1; i < words.length; i++) { tempBuff.append('.' + words[i].getText()); }
1723 String temp = tempBuff.toString();
1724
1725
1726 //make sure this imported package does not match the current package
1727 if (_package.equals(temp)) {
1728 _addAndIgnoreError("You do not need to import package " + temp +
1729 ". It is your package so all public classes in it are already visible.", that);
1730 return null;
1731 }
1732
1733 if (! _importedPackages.contains(temp)) _importedPackages.addLast(temp);
1734
1735 return forImportStatementOnly(that);
1736 }
1737
1738 /** Makes sure that this concrete method def is not declared to be abstract. */
1739 public Void forConcreteMethodDefDoFirst(ConcreteMethodDef that) {
1740 ModifiersAndVisibility mav = that.getMav();
1741 String[] modifiers = mav.getModifiers();
1742 // Concrete methods can be public, private, protected, or static at the Intermediate (Functional) level.
1743 for (int i = 0; i < modifiers.length; i++) {
1744 if (modifiers[i].equals("abstract")) {
1745 _addError("Methods that have a braced body cannot be declared \"abstract\"", that);
1746 break;
1747 }
1748 }
1749 return super.forConcreteMethodDefDoFirst(that);
1750 }
1751
1752 /** Makes sure that this abstract method def is not declared to be static. */
1753 public Void forAbstractMethodDefDoFirst(AbstractMethodDef that) {
1754 ModifiersAndVisibility mav = that.getMav();
1755 String[] modifiers = mav.getModifiers();
1756 // Concrete methods can now be public, private, protected at the Intermediate level. They still cannot be static.
1757 if (Utilities.isStatic(modifiers)) _badModifiers("static", "abstract", that);
1758 return super.forAbstractMethodDefDoFirst(that);
1759 }
1760
1761 /** Bitwise operators are allowed in Full Java */
1762 public Void forShiftAssignmentExpressionDoFirst(ShiftAssignmentExpression that) { return null; }
1763 public Void forBitwiseAssignmentExpressionDoFirst(BitwiseAssignmentExpression that) { return null; }
1764 public Void forBitwiseBinaryExpressionDoFirst(BitwiseBinaryExpression that) { return null; }
1765 public Void forBitwiseOrExpressionDoFirst(BitwiseOrExpression that) { return null; }
1766 public Void forBitwiseXorExpressionDoFirst(BitwiseXorExpression that) { return null; }
1767 public Void forBitwiseAndExpressionDoFirst(BitwiseAndExpression that) { return null; }
1768 public Void forBitwiseNotExpressionDoFirst(BitwiseNotExpression that) { return null; }
1769 public Void forShiftBinaryExpressionDoFirst(ShiftBinaryExpression that) { return null; }
1770 public Void forBitwiseNotExpressionDoFirst(ShiftBinaryExpression that) { return null; }
1771
1772 /** The EmptyExpression is a sign of an error. It means that we were missing something
1773 * we needed when the parser built the AST*/
1774 public Void forEmptyExpressionDoFirst(EmptyExpression that) {
1775 _addAndIgnoreError("You appear to be missing an expression here", that);
1776 return null;
1777 }
1778
1779 /** The NoOp expression signifies a missing binary operator that was encountered when the
1780 * parser built the AST. */
1781 public Void forNoOpExpressionDoFirst(NoOpExpression that) {
1782 _addAndIgnoreError("You are missing a binary operator here", that);
1783 return null;
1784 }
1785
1786 /** If a ClassDef defined in this source file is a TestCase class, make sure it is the only thing in the file. */
1787 public Void forSourceFileDoFirst(SourceFile that) {
1788
1789 for (int i = 0; i < that.getTypes().length; i++) {
1790 if (that.getTypes()[i] instanceof ClassDef) {
1791 ClassDef c = (ClassDef) that.getTypes()[i];
1792 String superName = c.getSuperclass().getName();
1793 if (superName.equals("TestCase") || superName.equals("junit.framework.TestCase")) {
1794 // TODO; add code to exclude the following test for FullJava files
1795 if (that.getTypes().length > 1) {
1796 _addAndIgnoreError("TestCases must appear in files by themselves in functional code", c);
1797 }
1798 }
1799 }
1800 }
1801 return null;
1802 }
1803
1804 /** Check to make sure there aren't any immediate errors in this SourceFile by calling the
1805 * doFirst method. Then, check to make sure that java.lang is imported, and if it is not, add
1806 * it to the list of importedpackages, since it is imported by default. Make a list of all classes
1807 * defined in this file.
1808 * Then, visit them one by one.
1809 */
1810 public Void forSourceFile(SourceFile that) {
1811 // System.err.println("Processing source file " + that.getSourceInfo().getFile());
1812 forSourceFileDoFirst(that); // Confirms that TestCase classes appear alone in files
1813 if (prune(that)) return null;
1814
1815 // The parser enforces that there is either zero or one PackageStatement.
1816 for (int i = 0; i < that.getPackageStatements().length; i++) that.getPackageStatements()[i].visit(this);
1817 for (int i = 0; i < that.getImportStatements().length; i++) that.getImportStatements()[i].visit(this);
1818 if (! _importedPackages.contains("java.lang")) _importedPackages.addFirst("java.lang");
1819
1820 TypeDefBase[] types = that.getTypes();
1821 // store the qualified names of all classes defined in this file in:
1822 _classesInThisFile = new HashSet<String>();
1823 for (int i = 0; i < types.length; i++) {
1824 // TODO: Add inner classes to this list?
1825
1826 String qualifiedClassName = getQualifiedClassName(types[i].getName().getText());
1827 _classesInThisFile.add(qualifiedClassName);
1828 // System.err.println("Adding " + qualifiedClassName + " to _classesInThisFile");
1829 _log.log("Adding " + qualifiedClassName + " to _classesInThisFile");
1830 }
1831
1832 for (int i = 0; i < types.length; i++) {
1833 // Remove the class that is about to be visited from the list of ClassDefs in this file.
1834 String qualifiedClassName = getQualifiedClassName(types[i].getName().getText());
1835 // Only visit a class if _classesInThisFile contains it. Otherwise, this class has
1836 // already been processed since it was a superclass of a previous class.
1837 if (_classesInThisFile.contains(qualifiedClassName)) {
1838 types[i].visit(this);
1839 }
1840 }
1841
1842 return forSourceFileOnly(that);
1843 }
1844
1845 /** Call the ResolveNameVisitor to see if this is a reference to a Type name. */
1846 public Void forSimpleNameReference(SimpleNameReference that) {
1847 that.visit(new ResolveNameVisitor());
1848 return null;
1849 }
1850
1851 /** Call the ResolveNameVisitor to see if this is a reference to a Type name. */
1852 public Void forComplexNameReference(ComplexNameReference that) {
1853 that.visit(new ResolveNameVisitor());
1854 return null;
1855 }
1856
1857 /** Do nothing. This is handled in the forVariableDeclarationOnly case.*/
1858 public Void forVariableDeclaration(VariableDeclaration that) {
1859 // System.err.println("forVariableDeclaration in LLV called for " + that);
1860 forVariableDeclarationDoFirst(that);
1861
1862 if (prune(that)) return null;
1863 // System.err.println("forVariableDeclarationDoFirst(...) completed with no errors");
1864 that.getMav().visit(this);
1865 // System.err.println("Mav visit completed in forVariableDeclaration; getClass() = " + getClass());
1866 return forVariableDeclarationOnly(that);
1867 }
1868
1869 /** If the method being generated already exists in the SymbolData,
1870 * throw an error, because generated methods cannot be overwritten.
1871 */
1872 protected static void addGeneratedMethod(SymbolData sd, MethodData md) {
1873 MethodData rmd = SymbolData.repeatedSignature(sd.getMethods(), md);
1874 if (rmd == null) {
1875 sd.addMethod(md, true);
1876 md.setGenerated(true);
1877 }
1878
1879 else if (!(getUnqualifiedClassName(sd.getName()).equals(md.getName()))) {
1880 //if it is not a constructor, it cannot be overridden--give an error
1881 _addAndIgnoreError("The method " + md.getName() + " is automatically generated, and thus you cannot override it",
1882 rmd.getJExpression());
1883 }
1884 }
1885
1886 /** Creates the automatically generated constructor for this class. It needs to take in the same arguments as its
1887 * super class' constructor as well as its fields. If there are multiple constructors in the super class, pick the
1888 * one with the least number of parameters. No constructor is created if this is an advanced level file (overridden
1889 * at advanced level), because no code augmentation is done.
1890 */
1891 public void createConstructor(SymbolData sd) {
1892 if (LanguageLevelConverter.isAdvancedFile(_file)) return;
1893
1894 // System.err.println("**** createConstructor called for " + sd);
1895
1896 // if (sd == null) {
1897 // System.err.println("**** Error **** After fixups, SymbolData " + sd + " has null for a super class");
1898 // assert false;
1899 // }
1900 //
1901 if (sd.isContinuation()) {
1902 _addAndIgnoreError("Could not generate constructor for class " + sd + " because it has no definition",
1903 new NullLiteral(SourceInfo.NONE));
1904 return;
1905 }
1906
1907 SymbolData superSd = sd.getSuperClass();
1908 if (superSd == null) {
1909 _addAndIgnoreError("Could not generate constructor for class " + sd + " because it has no superclass",
1910 new NullLiteral(SourceInfo.NONE));
1911 return;
1912 }
1913
1914 else {
1915 LinkedList<MethodData> superMethods = superSd.getMethods();
1916 String superUnqualifiedName = getUnqualifiedClassName(superSd.getName());
1917
1918 LanguageLevelVisitor sslv = LanguageLevelConverter._newSDs.remove(superSd);
1919
1920 // if sslv == null, the superclass constructor has already been generated or we are caught in a cyclic
1921 // inheritance hierarchy
1922 if (sslv != null) {
1923 sslv.createConstructor(superSd);
1924 // System.err.println("Creating constructor for superclass " + superSd);
1925 }
1926
1927 // Find the super's smallest constructor.
1928 MethodData superConstructor = null;
1929 for (MethodData superMd: superMethods) {
1930 // Iterator<MethodData> iter = superMethods.iterator();
1931 // while (iter.hasNext()) {
1932 // MethodData superMd = iter.next();
1933 if (superMd.getName().equals(superUnqualifiedName)) {
1934 if (superConstructor == null || superMd.getParams().length < superConstructor.getParams().length) {
1935 superConstructor = superMd;
1936 }
1937 }
1938 }
1939 if (superConstructor == null) {
1940 _addAndIgnoreError("Could not generate constructor for class " + sd + " superclass has no constructor, perhaps"
1941 + " because the class hierarchy is cyclic.",
1942 new NullLiteral(SourceInfo.NONE));
1943 return;
1944 }
1945 // if (superConstructor == null) {
1946 // System.err.println("**** Error **** The superclass " + superSd + " has no constructors ");
1947 // }
1948 String name = getUnqualifiedClassName(sd.getName());
1949 MethodData md = new MethodData(name,
1950 PUBLIC_MAV,
1951 new TypeParameter[0],
1952 sd,
1953 new VariableData[0], // Parameters to be filled in later.
1954 new String[0],
1955 sd,
1956 null);
1957
1958 LinkedList<VariableData> params = new LinkedList<VariableData>();
1959 // if (superConstructor != null) {
1960 for (VariableData superParam : superConstructor.getParams()) {
1961 String paramName = md.createUniqueName("super_" + superParam.getName());
1962 SymbolData superParamSD = superParam.getType();
1963 assert superParamSD != null;
1964 VariableData newParam = new VariableData(paramName, PACKAGE_MAV, superParamSD, true, sd); // Note: sd was md
1965 newParam.setGenerated(true);
1966 params.add(newParam);
1967 // Next line done on each iteration so that createUniqueName handles nameless super parameters (in class files)
1968 md.addVar(newParam); // Fixups have already been executed
1969 }
1970 // }
1971
1972 // only add in those fields that do not have a value and are not static.
1973 boolean hasOtherConstructor = sd.hasMethod(name);
1974
1975 for (VariableData field : sd.getVars()) {
1976
1977 if (! field.hasInitializer() && ! field.hasModifier("static")) {
1978 if (! hasOtherConstructor) { field.gotValue(); } // Set hasValue if no other constructors need to be visited
1979 // Rather than creating a new parameter, we use the field, since all the important data is the same in both of
1980 // them.
1981 VariableData param = field.copyWithoutVisibility();
1982 params.add(param);
1983 }
1984 }
1985 // Some fields may be declared private, but parameters cannot be; unprivatize the
1986
1987 md.setParams(params.toArray(new VariableData[params.size()]));
1988 md.setVars(params);
1989
1990 // System.err.println("**** Adding constructor " + md + " **** to symbol " + sd);
1991 // if (md.getName().equals("ClassName"))
1992 // System.err.println("****** constructor visibility = " + md.getMav());
1993 addGeneratedMethod(sd, md);
1994 }
1995 LanguageLevelConverter._newSDs.remove(sd); // this won't do anything if sd is not in _newSDs.
1996 }
1997
1998 /** Create a method that is an accessor for each field in the class.
1999 * File file is passed in so this can remain a static method
2000 * TODO: should this be called AFTER all fixups have been performed? No method needs to be in
2001 * symbol table.
2002 */
2003 protected static void createAccessors(SymbolData sd, File file) {
2004 if (LanguageLevelConverter.isAdvancedFile(file)) return;
2005 LinkedList<VariableData> fields = sd.getVars();
2006 for (final VariableData vd: fields) {
2007 if (! vd.hasModifier("static")) {
2008 String name = getFieldAccessorName(vd.getName());
2009 SymbolData returnTypeSD = vd.getType();
2010 final MethodData md = new MethodData(name,
2011 PUBLIC_MAV,
2012 new TypeParameter[0],
2013 returnTypeSD,
2014 new VariableData[0],
2015 new String[0],
2016 sd,
2017 null); // no SourceInfo
2018 addGeneratedMethod(sd, md);
2019 if (returnTypeSD == null) { // create a fixup to patch the return type of md; vd may have pending return type
2020 Command fixUp = new Command() {
2021 public void execute() { md.setReturnType(vd.getType()); }
2022 };
2023 fixUps.add(fixUp);
2024 }
2025 }
2026 }
2027 }
2028
2029 /** Create a method called toString that returns type String. Overridden at the Advanced Level files, because n code
2030 * augmentation is done for them so you don't want to create this method.
2031 */
2032 protected void createToString(SymbolData sd) {
2033 String name = "toString";
2034 MethodData md = new MethodData(name,
2035 PUBLIC_MAV,
2036 new TypeParameter[0],
2037 getSymbolData("java.lang.String", SourceInfo.make("java.lang.String")),
2038 new VariableData[0],
2039 new String[0],
2040 sd,
2041 null); // no SourceInfo
2042 addGeneratedMethod(sd, md);
2043 }
2044
2045 /** Creates a method called hashCode that returns an int. Overriden for FullJava files, because no code augmentation
2046 * is done for them, so we don't want to create this method.
2047 */
2048 protected void createHashCode(SymbolData sd) {
2049 String name = "hashCode";
2050 MethodData md = new MethodData(name,
2051 PUBLIC_MAV,
2052 new TypeParameter[0],
2053 SymbolData.INT_TYPE,
2054 new VariableData[0],
2055 new String[0],
2056 sd,
2057 null); // no SourceInfo
2058 addGeneratedMethod(sd, md);
2059 }
2060
2061 /** Creates a method called equals() that takes in an Object argument and returns a boolean. Overriden for FullJava
2062 * files, because no code augmentation is done for them, so we don't want to create this method.
2063 */
2064 protected void createEquals(SymbolData sd) {
2065 String name = "equals";
2066 SymbolData type = getSymbolData("java.lang.Object", SourceInfo.make("java.lang.Object"));
2067 VariableData param = new VariableData(type);
2068 MethodData md = new MethodData(name,
2069 PUBLIC_MAV,
2070 new TypeParameter[0],
2071 SymbolData.BOOLEAN_TYPE,
2072 new VariableData[] {param},
2073 new String[0],
2074 sd,
2075 null); // no SourceInfo
2076 param.setEnclosingData(md);
2077 addGeneratedMethod(sd, md);
2078 }
2079
2080 /**
2081 * This is overwritten because we don't want to visit each half of
2082 * MemberType recursively. Just take the whole thing and look for it in
2083 * forMemberTypeOnly (calls forTypeOnly eventually to get looked up).
2084 */
2085 public Void forMemberType(MemberType that) {
2086 forMemberTypeDoFirst(that);
2087 if (prune(that)) return null;
2088 return forMemberTypeOnly(that);
2089 }
2090
2091 /**Return the SymbolData for java.lang.String by default*/
2092 public Void forStringLiteralOnly(StringLiteral that) {
2093 getQualifiedSymbolData("java.lang.String", that.getSourceInfo(), true);
2094 return null;
2095 }
2096
2097 /** Try to resolve the type of the instantiation, and make sure there are no errors*/
2098 public Void forSimpleNamedClassInstantiation(SimpleNamedClassInstantiation that) {
2099 forSimpleNamedClassInstantiationDoFirst(that);
2100 if (prune(that)) return null;
2101 that.getType().visit(this);
2102 that.getArguments().visit(this);
2103
2104 // Put the allocated type into the symbol table
2105 /* TODO!: Shouldn't this happen for all Instantiations?
2106 * Even for all Types, regardless of where they show up?
2107 */
2108 getSymbolData(that.getType().getName(), that.getSourceInfo());
2109
2110 // TODO? create a fixup?
2111
2112 return forSimpleNamedClassInstantiationOnly(that);
2113 }
2114
2115
2116 /** Determines whether array1 equals array2 using the equals method on Object[] arrays in java.util.Arrays.
2117 * @return true if the two array argument (which may be null) are equal.
2118 */
2119 public static boolean arrayEquals(Object[] array1, Object[] array2) {
2120 // return Arrays.equals(array1, array2);
2121 int n = array1.length;
2122 if (n != array2.length) return false;
2123 for (int i = 0; i < n; i++) {
2124 Object o1 = array1[i];
2125 Object o2 = array2[i];
2126 if (o1 == null && o2 != null) return false;
2127 if (! o1.equals(o2)) return false;
2128 };
2129 return true;
2130 }
2131
2132 /** Use this to see if a name references a type that needs to be added to the symbolTable. */
2133 private class ResolveNameVisitor extends JExpressionIFAbstractVisitor<TypeData> {
2134
2135 public ResolveNameVisitor() { }
2136
2137 /** Most expressions are not relevant for this check--visit them with outer visitor. */
2138 public TypeData defaultCase(JExpressionIF that) {
2139 that.visit(LanguageLevelVisitor.this);
2140 return null;
2141 }
2142
2143 /** Try to look up this simple name reference and match it to a symbol data. If it could not be matched, return a
2144 * package data.
2145 * @param that The thing we're trying to match to a type
2146 */
2147 public TypeData forSimpleNameReference(SimpleNameReference that) {
2148 SymbolData result = getSymbolData(that.getName().getText(), that.getSourceInfo());
2149 // it could not be resolved: return a Package Data
2150 // TODO: create a fixup !!!
2151 if (result == SymbolData.NOT_FOUND) {
2152 return new PackageData(that.getName().getText());
2153 }
2154 return result;
2155 }
2156
2157 /** Try to look up the enclosing of this complex name reference and then try to match the name on the right
2158 * within that context to a type. If it could not be matched, return a package data.
2159 * @param that The thing we're trying to match to a type
2160 */
2161 public TypeData forComplexNameReference(ComplexNameReference that) {
2162 TypeData lhs = that.getEnclosing().visit(this);
2163 SymbolData result = getSymbolData(lhs, that.getName().getText(), that.getSourceInfo(), true);
2164
2165 // TODO: create a fixup?
2166 if (result == SymbolData.NOT_FOUND) {
2167 if (lhs instanceof PackageData) {
2168 return new PackageData((PackageData) lhs, that.getName().getText());
2169 }
2170 return null;
2171 }
2172 return result;
2173 }
2174 }
2175
2176 /** Test the methods defined in the above class.*/
2177 public static class LanguageLevelVisitorTest extends TestCase {
2178
2179 private LanguageLevelVisitor testLLVisitor;
2180 private Hashtable<SymbolData, LanguageLevelVisitor> testNewSDs;
2181
2182 private SymbolData _sd1;
2183 private SymbolData _sd2;
2184 private SymbolData _sd3;
2185 private SymbolData _sd4;
2186 private SymbolData _sd5;
2187 private SymbolData _sd6;
2188
2189 public LanguageLevelVisitorTest() { this(""); }
2190 public LanguageLevelVisitorTest(String name) { super(name); }
2191
2192 public void setUp() {
2193 Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>> continuations =
2194 new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>();
2195 LinkedList<Command> fixUps = new LinkedList<Command>();
2196 // The following ensures that essential symbols have been loaded into the symbolTable, a static field of
2197 // LanguageLevelConverter.
2198 testLLVisitor = new LanguageLevelVisitor(new File(""),
2199 "",
2200 "i.like.monkey",
2201 new LinkedList<String>(),
2202 new LinkedList<String>(),
2203 new HashSet<String>(),
2204 continuations,
2205 fixUps);
2206 errors = new LinkedList<Pair<String, JExpressionIF>>();
2207 _errorAdded = false;
2208
2209 visitedFiles = new LinkedList<Pair<LanguageLevelVisitor, SourceFile>>();
2210 // _hierarchy = new Hashtable<String, TypeDefBase>();
2211 testLLVisitor._classesInThisFile = new HashSet<String>();
2212 if (! testLLVisitor._importedPackages.contains("java.lang")) testLLVisitor._importedPackages.add("java.lang");
2213
2214 _sd1 = new SymbolData("i.like.monkey");
2215 _sd2 = new SymbolData("i.like.giraffe");
2216 _sd3 = new SymbolData("zebra");
2217 _sd4 = new SymbolData("u.like.emu");
2218 _sd5 = new SymbolData("");
2219 _sd6 = new SymbolData("cebu");
2220 }
2221
2222 /** Tests the getUnqualifiedClassName method. */
2223 public void testGetUnqualifiedClassName() {
2224 assertEquals("getUnqualifiedClassName with a qualified name with an inner class", "innermonkey",
2225 testLLVisitor.getUnqualifiedClassName("i.like.monkey$innermonkey"));
2226 assertEquals("getUnqualifiedClassName with a qualified name", "monkey",
2227 testLLVisitor.getUnqualifiedClassName("i.like.monkey"));
2228 assertEquals("getUnqualifiedClassName with an unqualified name", "monkey",
2229 testLLVisitor.getUnqualifiedClassName("monkey"));
2230 assertEquals("getUnqualifiedClassName with an empty string", "", testLLVisitor.getUnqualifiedClassName(""));
2231 }
2232
2233 public void testClassFile2SymbolData() {
2234
2235 //test a java.lang symbol data
2236 SymbolData objectSD = LanguageLevelConverter._classFile2SymbolData("java.lang.Object", "");
2237 SymbolData stringSD = LanguageLevelConverter._classFile2SymbolData("java.lang.String", "");
2238 MethodData md = new MethodData("substring", PUBLIC_MAV, new TypeParameter[0], stringSD,
2239 new VariableData[] {new VariableData(SymbolData.INT_TYPE)},
2240 new String[0], stringSD, null);
2241 assertTrue("java.lang.String should have been converted successfully",
2242 stringSD.getName().equals("java.lang.String"));
2243 assertEquals("java.lang.String's superSD should should be java.lang.Object",
2244 objectSD,
2245 stringSD.getSuperClass());
2246
2247 LinkedList<MethodData> methods = stringSD.getMethods();
2248 Iterator<MethodData> iter = methods.iterator();
2249 boolean found = false;
2250
2251 while (iter.hasNext()) {
2252 MethodData currMd = iter.next();
2253 if (currMd.getName().equals("substring") && currMd.getParams().length == 1 &&
2254 currMd.getParams()[0].getInstanceData() == SymbolData.INT_TYPE.getInstanceData()) {
2255 // TODO: comparing getInstanceData() on preceding line is goofy; should check getType() instead
2256 found = true;
2257 md.getParams()[0].setEnclosingData(currMd);
2258 break;
2259 }
2260 }
2261
2262 assertTrue("Should have found method substring(int) in java.lang.String", found);
2263
2264 assertEquals("java.lang.String should be packaged correctly", "java.lang",
2265 testLLVisitor.getSymbolData("java.lang.String", SourceInfo.NONE).getPackage());
2266
2267 //now, test that a second call to the same method won't replace the symbol data that is already there.
2268 SymbolData newStringSD = LanguageLevelConverter._classFile2SymbolData("java.lang.String", "");
2269 assertTrue("Second call to classFileToSymbolData should not change sd in hash table.",
2270 stringSD == LanguageLevelConverter.symbolTable.get("java.lang.String"));
2271 assertTrue("Second call to classFileToSymbolData should return same SD.",
2272 newStringSD == LanguageLevelConverter.symbolTable.get("java.lang.String"));
2273 //now, test one of our own small class files.
2274
2275 SymbolData bartSD = LanguageLevelConverter._classFile2SymbolData("Bart", "testFiles");
2276 assertFalse("bartSD should not be null", bartSD == null);
2277 assertFalse("bartSD should not be a continuation", bartSD.isContinuation());
2278 MethodData md1 =
2279 new MethodData("myMethod", PROTECTED_MAV,
2280 new TypeParameter[0], SymbolData.BOOLEAN_TYPE,
2281 new VariableData[] { new VariableData(SymbolData.INT_TYPE) },
2282 new String[] {"java.lang.Exception"}, bartSD, null);
2283
2284 md1.getParams()[0].setEnclosingData(bartSD.getMethods().getLast());
2285 MethodData md2 = new MethodData("Bart", PUBLIC_MAV, new TypeParameter[0], bartSD,
2286 new VariableData[0], new String[0], bartSD, null);
2287
2288 VariableData vd1 = new VariableData("i", PUBLIC_MAV, SymbolData.INT_TYPE, true, bartSD);
2289
2290 LinkedList<MethodData> bartsMD = new LinkedList<MethodData>();
2291 bartsMD.addFirst(md1);
2292 bartsMD.addFirst(md2);
2293
2294 LinkedList<VariableData> bartsVD = new LinkedList<VariableData>();
2295 bartsVD.addLast(vd1);
2296
2297 assertEquals("Bart's super class should be java.lang.Object: errors = " + errors, objectSD,
2298 bartSD.getSuperClass());
2299 assertEquals("Bart's Variable Data should be a linked list containing only vd1", bartsVD, bartSD.getVars());
2300 assertEquals("The first method data of bart's should be correct", md2, bartSD.getMethods().getFirst());
2301
2302 assertEquals("The second method data of bart's should be correct", md1, bartSD.getMethods().getLast());
2303 assertEquals("Bart's Method Data should be a linked list containing only md1", bartsMD, bartSD.getMethods());
2304 }
2305
2306 // public void testLookupFromClassesToBeParsed() {
2307 // // Create a ClassDef. Recreate the ClassOrInterfaceType for Object instead of using
2308 // // JExprParser.NO_TYPE since otherwise the ElementaryVisitor will complain that the
2309 // // user must explicitly extend Object.
2310 // ClassDef cd =
2311 // new ClassDef(SourceInfo.NONE, PUBLIC_MAV,
2312 // new Word(SourceInfo.NONE, "Lisa"),
2313 // new TypeParameter[0],
2314 // new ClassOrInterfaceType(SourceInfo.NONE, "java.lang.Object", new Type[0]),
2315 // new ReferenceType[0],
2316 // new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
2317 //
2318 // // Use a ElementaryVisitor so lookupFromClassesToBeParsed will actually visit the ClassDef.
2319 // IntermediateVisitor bv = new IntermediateVisitor(new File(""),
2320 // errors,
2321 // continuations,
2322 // new LinkedList<Pair<LanguageLevelVisitor, SourceFile>>());
2323 //
2324 // // Test that passing resolve equals false returns a continuation.
2325 //// assertTrue("Should return a continuation",
2326 //// testLLVisitor._identifyTypeFromClassesToBeParsed("Lisa", SourceInfo.NONE, false).isContinuation());
2327 // // Put Lisa in the hierarchy and test that there is one error and that the message
2328 // // says that there is cyclic inheritance.
2329 //// _hierarchy.put("Lisa", cd);
2330 //// _classesInThisFile.put("Lisa", new Pair<TypeDefBase, LanguageLevelVisitor>(cd, bv));
2331 //// assertEquals("Should return null because Lisa is in the hierarchy",
2332 //// null,
2333 //// _llv._identifyTypeFromClassesToBeParsed("Lisa", SourceInfo.NONE, true));
2334 //// assertEquals("Should be one error", 1, errors.size());
2335 //// assertEquals("Error message should be correct", "Cyclic inheritance involving Lisa", errors.get(0).getFirst());
2336 //// _hierarchy.remove("Lisa");
2337 // //Re-add Lisa because the first call with resolve set to true removed it and
2338 // // test that Lisa is actually visited and added to the symbolTable.
2339 // testLLVisitor._classesInThisFile.put("Lisa", new Pair<TypeDefBase, LanguageLevelVisitor>(cd, bv));
2340 // assertFalse("Should return a non-continuation",
2341 // testLLVisitor._identifyTypeFromClassesToBeParsed("Lisa",
2342 // SourceInfo.NONE,
2343 // true).isContinuation());
2344 // }
2345
2346 public void testGetSymbolDataForClassFile() {
2347 // Test that passing a legal class return a non-continuation.
2348 assertFalse("Should return a non-continuation",
2349 LanguageLevelConverter.getSymbolDataForClassFile("java.lang.String", null).isContinuation());
2350
2351 // Test that passing a userclass that can't be found returns null and adds an error.
2352 assertNull("Should return null with a user class that can't be found",
2353 LanguageLevelConverter.getSymbolDataForClassFile("Marge", null));
2354 // assertEquals("There should be one error", 1, errors.size());
2355 // assertEquals("The error message should be correct", "Class Marge not found.", errors.get(0).getFirst());
2356 }
2357
2358
2359 public void testGetSymbolData_Primitive() {
2360 assertEquals("should be boolean type", SymbolData.BOOLEAN_TYPE,
2361 LanguageLevelConverter._getPrimitiveSymbolData("boolean"));
2362 assertEquals("should be char type", SymbolData.CHAR_TYPE, LanguageLevelConverter._getPrimitiveSymbolData("char"));
2363 assertEquals("should be byte type", SymbolData.BYTE_TYPE, LanguageLevelConverter._getPrimitiveSymbolData("byte"));
2364 assertEquals("should be short type", SymbolData.SHORT_TYPE, LanguageLevelConverter._getPrimitiveSymbolData("short"));
2365 assertEquals("should be int type", SymbolData.INT_TYPE, LanguageLevelConverter._getPrimitiveSymbolData("int"));
2366 assertEquals("should be long type", SymbolData.LONG_TYPE, LanguageLevelConverter._getPrimitiveSymbolData("long"));
2367 assertEquals("should be float type", SymbolData.FLOAT_TYPE, LanguageLevelConverter._getPrimitiveSymbolData("float"));
2368 assertEquals("should be double type", SymbolData.DOUBLE_TYPE, LanguageLevelConverter._getPrimitiveSymbolData("double"));
2369 assertEquals("should be void type", SymbolData.VOID_TYPE, LanguageLevelConverter._getPrimitiveSymbolData("void"));
2370 assertEquals("should be null type", SymbolData.NULL_TYPE, LanguageLevelConverter._getPrimitiveSymbolData("null"));
2371 assertEquals("should return null--not a primitive", null,
2372 LanguageLevelConverter._getPrimitiveSymbolData("java.lang.String"));
2373 }
2374
2375 // public void testGetQualifiedSymbolData() {
2376 // testLLVisitor._file = new File("testFiles/Fake.dj0");
2377 // SymbolData sd = new SymbolData("testPackage.File");
2378 // testLLVisitor._package = "testPackage";
2379 // LanguageLevelConverter.symbolTable.put("testPackage.File", sd);
2380 //
2381 // SymbolData sd1 = new SymbolData("java.lang.String");
2382 // LanguageLevelConverter.symbolTable.put("java.lang.String", sd1);
2383 //
2384 // //Test that classes not in the symbol table are handled correctly.
2385 // assertEquals("should the continuation symbol", sd,
2386 // testLLVisitor._getQualifiedSymbolData("testPackage.File", SourceInfo.NONE, true, false, true));
2387 //// assertEquals("should be one error so far.", 1, errors.size());
2388 //
2389 //
2390 // SymbolData sd2 = testLLVisitor._getQualifiedSymbolData("java.lang.Integer", SourceInfo.NONE, true, true, true);
2391 // assertEquals("should return non-continuation java.lang.Integer", "java.lang.Integer", sd2.getName());
2392 // assertFalse("should not be a continuation.", sd2.isContinuation());
2393 //
2394 // SymbolData sd3 = testLLVisitor._getQualifiedSymbolData("Wow", SourceInfo.NONE, true, true, true);
2395 // assertEquals("search should fail", null, sd3);
2396 //// assertEquals("should return Wow", "Wow", sd3.getName());
2397 //// assertFalse("Should not be a continuation.", sd3.isContinuation());
2398 //
2399 // // "testPackage.File" has been entered as a continuation in symbolTable. Why should the following lookup fail?
2400 //// //Test that classes in the symbol table are handled correctly
2401 //// assertEquals("should return null sd--does not exist", null,
2402 //// _llv._getQualifiedSymbolData("testPackage.File", SourceInfo.NONE, false, false, true));
2403 //// assertEquals("Should be 1 error", 1, errors.size());
2404 //
2405 // sd.setIsContinuation(false);
2406 // assertEquals("should return non-continuation sd", sd,
2407 // testLLVisitor._getQualifiedSymbolData("testPackage.File", SourceInfo.NONE, true, false, true));
2408 //
2409 //
2410 // assertEquals("Should return sd1.", sd1,
2411 // testLLVisitor._getQualifiedSymbolData("java.lang.String", SourceInfo.NONE, true, false, true));
2412 // assertFalse("sd1 should no longer be a continuation.", sd1.isContinuation());
2413 //
2414 //
2415 //
2416 // //check that stuff not in symbol table and packaged incorrectly is handled right.
2417 // assertEquals("should return null-because it's not a valid class", null,
2418 // testLLVisitor._getQualifiedSymbolData("testPackage.not.in.symboltable",
2419 // SourceInfo.NONE, true, false, true));
2420 //
2421 // assertEquals("should be two errors so far.", 2, errors.size());
2422 // assertNull("should return null",
2423 // testLLVisitor._getQualifiedSymbolData("testPackage.not.in.symboltable",
2424 // SourceInfo.NONE, false, false, false));
2425 //
2426 // assertNull("should return null.",
2427 // testLLVisitor._getQualifiedSymbolData("notRightPackage", SourceInfo.NONE, false, false, false));
2428 // assertEquals("should still be two errors.", 2, errors.size());
2429 // }
2430
2431 public void testGetArraySymbolData() {
2432 //Initially, force the inner sd of this array type to be null, to test that.
2433 assertEquals("Should return null, because inner sd is null.", null,
2434 testLLVisitor.getArraySymbolData("TestFile", SourceInfo.NONE, false, false));
2435
2436 /**Now, put a real SymbolData base in the table.*/
2437 SymbolData sd = new SymbolData("Iexist");
2438 LanguageLevelConverter.symbolTable.put("Iexist", sd);
2439 testLLVisitor.getArraySymbolData("Iexist", SourceInfo.NONE, false, false).getName();
2440 assertTrue("Should have created an array data and add it to symbol table.",
2441 LanguageLevelConverter.symbolTable.containsKey("Iexist[]"));
2442 SymbolData ad = LanguageLevelConverter.symbolTable.get("Iexist[]");
2443
2444 //make sure that ad has the appropriate fields and super classes and interfaces and methods
2445 assertEquals("Should only have field 'length'", 1, ad.getVars().size());
2446 assertNotNull("Should contain field 'length'", ad.getVar("length"));
2447
2448 assertEquals("Should only have one method-clone", 1, ad.getMethods().size());
2449 assertTrue("Should contain method clone", ad.hasMethod("clone"));
2450
2451 assertEquals("Should have Object as super class",
2452 LanguageLevelConverter.symbolTable.get("java.lang.Object"),
2453 ad.getSuperClass());
2454 assertEquals("Should have 2 interfaces", 2, ad.getInterfaces().size());
2455 assertEquals("Interface 1 should be java.lang.Cloneable", "java.lang.Cloneable",
2456 ad.getInterfaces().get(0).getName());
2457 assertEquals("Interface 2 should be java.io.Serializable", "java.io.Serializable",
2458 ad.getInterfaces().get(1).getName());
2459
2460
2461 /**Now try it with the full thing already in the symbol table.*/
2462 assertEquals("Since it's already in symbol table now, should just return it.", ad,
2463 testLLVisitor.getArraySymbolData("Iexist", SourceInfo.NONE, false, false));
2464
2465 /**Now, try it with a multiple dimension array.*/
2466 testLLVisitor.getArraySymbolData("Iexist[]", SourceInfo.NONE, false, false);
2467 assertTrue("Should have added a multidimensional array to the table.",
2468 LanguageLevelConverter.symbolTable.containsKey("Iexist[][]"));
2469
2470 SymbolData sd2 = new SymbolData("java.lang.String");
2471 LanguageLevelConverter.symbolTable.put("java.lang.String", sd2);
2472 testLLVisitor.getArraySymbolData("String[]", SourceInfo.NONE, false, true);
2473 assertTrue("Should have added java.lang.String[] to table",
2474 LanguageLevelConverter.symbolTable.containsKey("java.lang.String[]"));
2475 assertTrue("Should have added java.lang.String[][] to table",
2476 LanguageLevelConverter.symbolTable.containsKey("java.lang.String[][]"));
2477 }
2478 /** Tests _getSymbolDataFromFileSystem and one case of getQualifiedSymbolData. */
2479 public void testGetSymbolDataFromFileSystem() {
2480 _sd4.setIsContinuation(false);
2481 _sd6.setIsContinuation(true);
2482 LanguageLevelConverter.symbolTable.put("u.like.emu", _sd4);
2483 LanguageLevelConverter.symbolTable.put("cebu", _sd6);
2484
2485 // Test if it's already in the symbol table and doesn't need to be resolved not stopping when it should.
2486 // get error b/c not in classes to be parsed
2487 assertEquals("symbol data is a not a continuation, but resolve is false so should just be returned", _sd6,
2488 testLLVisitor._getSymbolDataFromFileSystem("cebu", SourceInfo.NONE, false, true));
2489 assertEquals("symbol data is a continuation, but resolve is false, so should just be returned.", _sd4,
2490 testLLVisitor._getSymbolDataFromFileSystem("u.like.emu", SourceInfo.NONE, false, true));
2491
2492 // Lookup a name not in the file system with resolve equal to false, to confirm that null is returned.
2493
2494 assertEquals("Should return SymbolData.NOT_FOUND", SymbolData.NOT_FOUND,
2495 testLLVisitor._getSymbolDataFromFileSystem("Corky", SourceInfo.NONE, false, true));
2496 // TODO: fix this test
2497 // SymbolData matchCorky = testLLVisitor._getSymbolDataFromFileSystem("Corky", SourceInfo.NONE, true);
2498 // assertFalse("Should return a non-continuation", matchCorky.isContinuation());
2499
2500 // Test if it needs to be resolved:
2501 ClassDef cd =
2502 new ClassDef(SourceInfo.NONE, PUBLIC_MAV,
2503 new Word(SourceInfo.NONE, "Lisa"),
2504 new TypeParameter[0],
2505 new ClassOrInterfaceType(SourceInfo.NONE, "java.lang.Object", new Type[0]),
2506 new ReferenceType[0],
2507 new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
2508
2509 // Use a ElementaryVisitor so lookupFromClassesToBeParsed will actually visit the ClassDef.
2510 IntermediateVisitor bv = new IntermediateVisitor(new File(""));
2511
2512 testLLVisitor._classesInThisFile.add("Lisa" /*, new Pair<TypeDefBase, LanguageLevelVisitor>(cd, bv)*/);
2513 assert testLLVisitor._classesInThisFile.contains("Lisa");
2514 SymbolData matchLisa =
2515 testLLVisitor.getQualifiedSymbolData("Lisa", SourceInfo.NONE, true);
2516 assertTrue("Should return a continuation", matchLisa.isContinuation());
2517 }
2518
2519 public void testGetSymbolDataFromFileSystem2() {
2520 //what if it is in classes to be parsed?
2521 //and what if the class we're looking up is in the same package as the current file?
2522 //qualified
2523 testLLVisitor._package="fully.qualified";
2524 testLLVisitor._file = new File("testFiles/fully/qualified/Fake.dj0");
2525 SymbolData sd2 = new SymbolData("fully.qualified.Woah"); // continuation
2526 testLLVisitor.symbolTable.put("fully.qualified.Woah", sd2);
2527
2528 SymbolData result =
2529 testLLVisitor._getSymbolDataFromFileSystem("fully.qualified.Woah", SourceInfo.NONE, false, true);
2530
2531 assertEquals("Should return sd2, unresolved.", sd2, result);
2532 assertTrue("sd2 should still be unresolved", sd2.isContinuation());
2533 assertEquals("Should be no errors", 0, errors.size());
2534
2535 // result = _llv._getSymbolDataFromFileSystem("fully.qualified.Woah", SourceInfo.NONE, false, true);
2536 // assertEquals("Should return sd2, now unresolved.", sd2, result);
2537 // assertTrue("sd2 should not be resolved", sd2.isContinuation());
2538 // assertEquals("Should be no errors", 0, errors.size());
2539
2540 // result = _llv._getSymbolDataFromFileSystem("fully.qualified.Woah", SourceInfo.NONE, true, true);
2541 // assertEquals("Should return sd2, now resolved.", sd2, result);
2542 // assertFalse("sd2 should now be resolved", sd2.isContinuation());
2543 // assertEquals("Should be no errors", 0, errors.size());
2544
2545
2546 //what if the files are in different packages
2547 testLLVisitor.symbolTable.remove("fully.qualified.Woah");
2548 testLLVisitor.visitedFiles.clear();
2549 testLLVisitor._package="another.package";
2550 testLLVisitor._file = new File("testFiles/another/package/Wowsers.dj0");
2551 sd2 = new SymbolData("fully.qualified.Woah");
2552 testLLVisitor.symbolTable.put("fully.qualified.Woah", sd2);
2553
2554 result = testLLVisitor._getSymbolDataFromFileSystem("fully.qualified.Woah", SourceInfo.NONE, false, true);
2555
2556 assertEquals("Should return sd2, unresolved.", sd2, result);
2557 assertTrue("sd2 should still be unresolved", sd2.isContinuation());
2558 assertEquals("Should be no errors", 0, errors.size());
2559
2560 // result = _llv._getSymbolDataFromFileSystem("fully.qualified.Woah", SourceInfo.NONE, true, true);
2561 // assertEquals("Should return sd2, now resolved.", sd2, result);
2562
2563 // assertFalse("sd2 should be resolved", sd2.isContinuation());
2564 // assertEquals("Should be no errors", 0, errors.size());
2565
2566 //what if there is no package
2567 //Now, check cases when desired SymbolData is in the symbol table.
2568 testLLVisitor._package = "";
2569 testLLVisitor._file = new File ("testFiles/Cool.dj0"); // non-existent file
2570
2571 //unqualified
2572 SymbolData sd1 = new SymbolData("Wow");
2573 SymbolData obj = testLLVisitor._getSymbolDataFromFileSystem("java.lang.Object", SourceInfo.NONE, true, true);
2574 sd1.setSuperClass(obj);
2575 testLLVisitor.symbolTable.put("Wow", sd1);
2576
2577 result = testLLVisitor._getSymbolDataFromFileSystem("Wow", SourceInfo.NONE, false, true);
2578 assertEquals("Should return sd1, unresolved.", sd1, result);
2579 assertTrue("sd1 should still be unresolved.", sd1.isContinuation());
2580 assertEquals("Should be no errors", 0, errors.size());
2581
2582 result = testLLVisitor._getSymbolDataFromFileSystem("Wow", SourceInfo.NONE, true, true);
2583 assertEquals("Should return sd1, resolved.", sd1, result);
2584 assertFalse("sd1 should be resolved.", sd1.isContinuation());
2585 assertEquals("Should be no errors", 0, errors.size());
2586
2587 result = testLLVisitor._getSymbolDataFromFileSystem("Wow", SourceInfo.NONE, true, true);
2588 assertEquals("Should return sd1.", sd1, result);
2589 assertFalse("sd1 should still be resolved.", sd1.isContinuation());
2590 assertEquals("Should be no errors", 0, errors.size());
2591
2592
2593 //finding the most recent file
2594 result = testLLVisitor._getSymbolDataFromFileSystem("James", SourceInfo.NONE, true, true);
2595 assertEquals("Search for James should fail", null, result);
2596 // assertEquals("Result should have 3 variables.", 3, result.getVars().size());
2597 // assertEquals("Should be no errors", 0, errors.size());
2598
2599 //returning NOT_FOUND when it doesn't exist.
2600 testLLVisitor._package = "myPackage";
2601 assertEquals("Should return NOT_FOUND-does not exist.",
2602 SymbolData.NOT_FOUND,
2603 testLLVisitor._getSymbolDataFromFileSystem("WrongPackage.className",
2604 SourceInfo.NONE, true, false));
2605 assertEquals("Should be no errors", 0, errors.size());
2606
2607 // Now, test case where class file still exists, but java file is gone.
2608 testLLVisitor._package = "";
2609 testLLVisitor._file = new File("testFiles/Fake.dj0");
2610 LinkedList<VariableData> vds = new LinkedList<VariableData>();
2611 result = testLLVisitor._getSymbolDataFromFileSystem("Doh", SourceInfo.NONE, true, true);
2612 vds.addLast(new VariableData("i", PACKAGE_MAV, SymbolData.INT_TYPE, true, result));
2613 vds.addLast(new VariableData("o", PACKAGE_MAV, obj, true, result));
2614 // Since some list elements are arrays, comparison test is suspect
2615 // assertEquals("should have correct variable datas", vds, result.getVars());
2616 // assertFalse("should not be a continuation", result.isContinuation());
2617
2618 //Now test case where java file has been updated more recently than class file.
2619 //TODO: How can we test this since repository is checked out (i.e. the files all have the same timestamp)?
2620 }
2621
2622 public void testGetSymbolData() {
2623 testLLVisitor._package="";
2624 testLLVisitor._file = new File("testFiles/akdjskj");
2625
2626 // No dot case
2627 SymbolData sd1 = new SymbolData("Wow");
2628 testLLVisitor.symbolTable.put("Wow", sd1);
2629 assertEquals("Should return an equal SymbolData",
2630 sd1, testLLVisitor.getQualifiedSymbolData("Wow", SourceInfo.NONE, true));
2631 assertFalse("Should not be a continuation", sd1.isContinuation()); // There is a pre-existing class file Wow!
2632
2633 // Invalid case
2634 SymbolData result = testLLVisitor.getSymbolData("ima.invalid", SourceInfo.NONE, true, false);
2635 assertEquals("Should return null-invalid class name", null, result);
2636 assertEquals("There should not be any errors", 0, testLLVisitor.errors.size());
2637
2638 // Fully qualified class name
2639 testLLVisitor._package="fully.qualified";
2640 testLLVisitor._file = new File("testFiles/fully/qualified/Fake.dj0");
2641 SymbolData sd2 = new SymbolData("fully.qualified.Symbol");
2642 testLLVisitor.symbolTable.put("fully.qualified.Symbol", sd2);
2643
2644 result = testLLVisitor.getSymbolData("fully.qualified.Symbol", SourceInfo.NONE, true, false);
2645
2646 assertEquals("Should return sd2, resolved.", sd2, result);
2647 assertTrue("sd2 should be resolved", sd2.isContinuation());
2648
2649 // Inner class
2650 sd1.setName("fully.qualified.Woah.Wow");
2651 sd2.addInnerClass(sd1);
2652 sd1.setOuterData(sd2);
2653 testLLVisitor.symbolTable.put("fully.qualified.Woah.Wow", sd1);
2654 testLLVisitor.symbolTable.remove("Wow");
2655 sd1.setIsContinuation(false);
2656 result = testLLVisitor.getSymbolData("fully.qualified.Woah.Wow", SourceInfo.NONE, true, false);
2657 assertEquals("Should return sd1 (the inner class!)", sd1, result);
2658
2659 // Inner inner class
2660 SymbolData sd3 = new SymbolData("fully.qualified.Woah.Wow.James");
2661 sd1.addInnerClass(sd3);
2662 // System.err.println("SYMBOL TABLE ENTRY FOR \"fully.qualified.Woah.Wow.James\" = " +
2663 // _llv.symbolTable.get("fully.qualified.Woah.Wow.James"));
2664 sd3.setOuterData(sd1);
2665 // System.err.println("INNER CLASS LOOKUP YIELDS: " + sd1.getInnerClassOrInterface("James"));
2666 result = testLLVisitor.getSymbolData("fully.qualified.Woah.Wow.James", SourceInfo.NONE, true, false);
2667 assertEquals("Should return sd3", sd3, result);
2668 }
2669
2670 public void testGetQualifiedSymbolData() {
2671 // Primitive types
2672 assertEquals("should return the int SymbolData", SymbolData.INT_TYPE,
2673 testLLVisitor.getQualifiedSymbolData("int", SourceInfo.NONE, true, true, true));
2674 assertEquals("should return the byte SymbolData", SymbolData.BYTE_TYPE,
2675 testLLVisitor.getQualifiedSymbolData("byte", SourceInfo.NONE, false, false, false));
2676
2677 // Array types
2678 ArrayData ad = new ArrayData(SymbolData.INT_TYPE, testLLVisitor, SourceInfo.NONE);
2679 SymbolData result = testLLVisitor.getQualifiedSymbolData("int[]", SourceInfo.NONE, true, true, true);
2680 ad.getVars().get(0).setEnclosingData(result); //.equals(...) on VariableData compares enclosing datas with ==.
2681 ad.getMethods().get(0).setEnclosingData(result.getMethods().get(0).getEnclosingData()); //similar hack
2682 assertEquals("should return the array type", ad, result);
2683
2684 // Qualified types
2685 SymbolData sd = new SymbolData("java.lang.System");
2686 LanguageLevelConverter.symbolTable.put("java.lang.System", sd);
2687 assertEquals("should return the same sd", sd,
2688 testLLVisitor.getQualifiedSymbolData("java.lang.System", SourceInfo.NONE, false, true, true));
2689 assertTrue("should be a continuation", sd.isContinuation());
2690 assertEquals("should return the now resolved sd", sd,
2691 testLLVisitor.getQualifiedSymbolData("java.lang.System", SourceInfo.NONE, true, false, true));
2692 assertFalse("should not be a continuation", sd.isContinuation());
2693
2694 // In this file
2695 sd = new SymbolData("fully.qualified.Qwerty");
2696 LanguageLevelConverter.symbolTable.put("fully.qualified.Qwerty", sd);
2697 testLLVisitor._classesInThisFile.add("fully.qualified.Qwerty");
2698 // Use a ElementaryVisitor so lookupFromClassesToBeParsed will actually visit the ClassDef.
2699 IntermediateVisitor bv = new IntermediateVisitor(new File(""),
2700 errors,
2701 continuations,
2702 fixUps,
2703 new LinkedList<Pair<LanguageLevelVisitor, SourceFile>>());
2704 bv._package = "fully.qualified";
2705 bv._file = new File("testFiles/fully/qualified/Fake.dj0");
2706 ClassDef cd = new ClassDef(SourceInfo.NONE,
2707 PACKAGE_MAV,
2708 new Word(SourceInfo.NONE, "Qwerty"),
2709 new TypeParameter[0],
2710 new ClassOrInterfaceType(SourceInfo.NONE, "java.lang.Object", new Type[0]),
2711 new ReferenceType[0],
2712 new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
2713 bv._classesInThisFile.add("fully.qualified.Qwerty" /*, new Pair<TypeDefBase, LanguageLevelVisitor>(cd, bv)*/);
2714 assertEquals("should return sd the continuation", sd,
2715 bv.getSymbolData("Qwerty", SourceInfo.NONE, true, true));
2716 assertTrue("should be a continuation", sd.isContinuation());
2717 assertEquals("should also return a continuation", sd,
2718 bv.getQualifiedSymbolData("fully.qualified.Qwerty", SourceInfo.NONE, false, false, true));
2719 assertTrue("should be a continuation", sd.isContinuation());
2720
2721 // Imported files
2722 testLLVisitor._importedFiles.addLast("a.b.c");
2723 sd = new SymbolData("a.b.c");
2724 // System.err.println("SymbolData for 'a.b.c' is " + sd);
2725 LanguageLevelConverter.symbolTable.put("a.b.c", sd);
2726 // System.err.println("SymbolTable entry for 'a.b.c' is " + LanguageLevelConverter.symbolTable.get("a.b.c"));
2727 // LanguageLevelConverter.symbolTable.put("foobar", new SymbolData("This is strange"));
2728 // System.err.println("SymbolTable entry for 'foobar' is " + LanguageLevelConverter.symbolTable.get("foobar"));
2729 assertEquals("should find the continuation in the symbol table", sd,
2730 testLLVisitor.getQualifiedSymbolData("a.b.c", SourceInfo.NONE, false, true, true));
2731 // TODO: create an import table to look at when no match is found in symbolTable.
2732 assertTrue("should be a continuation", sd.isContinuation());
2733
2734 testLLVisitor._package="fully.qualified";
2735 testLLVisitor._file = new File("testFiles/fully/qualified/Fake.dj0");
2736 testLLVisitor._importedFiles.addLast("fully.qualified.Woah");
2737 SymbolData sd2 = new SymbolData("fully.qualified.Woah");
2738 sd2.setIsContinuation(false);
2739 LanguageLevelConverter.symbolTable.put("fully.qualified.Woah", sd2);
2740 result = testLLVisitor.getQualifiedSymbolData("fully.qualified.Woah", SourceInfo.NONE, true, false, true);
2741 // System.err.println("result for 'fully.qualifed.Woah' is " + result);
2742 assertEquals("should find the resolved symbol data in the symbol table", sd2, result);
2743 assertFalse("should not be a continuation", sd2.isContinuation());
2744
2745 // File System
2746 testLLVisitor._importedFiles.clear();
2747 testLLVisitor.visitedFiles.clear();
2748 LanguageLevelConverter.symbolTable.remove("fully.qualified.Woah");
2749 sd2 = new SymbolData("fully.qualified.Woah");
2750 LanguageLevelConverter.symbolTable.put("fully.qualified.Woah", sd2);
2751
2752 // System.err.println("_llv.getSymbolData for fully.qualified.Woah = " +
2753 // _llv.getQualifiedSymbolData("fully.qualified.Woah",
2754 // SourceInfo.NONE, true, true, true));
2755
2756 result = testLLVisitor.getQualifiedSymbolData("fully.qualified.Woah", SourceInfo.NONE, false, false, true);
2757
2758 assertEquals("Should return sd2, unresolved.", sd2, result);
2759 assertTrue("sd2 should still be unresolved", sd2.isContinuation());
2760
2761 result = testLLVisitor.getQualifiedSymbolData("fully.qualified.Woah", SourceInfo.NONE, false, false, true);
2762 assertEquals("Should return sd2, now unresolved.", sd2, result);
2763 assertTrue("sd2 should not be resolved", sd2.isContinuation());
2764
2765 // The following "test" forces the definition of "Woah" to be retrieved from the file system but THERE IS NO CLASS
2766 // FILE so the file system search returns null!
2767 result = testLLVisitor.getQualifiedSymbolData("fully.qualified.Woah", SourceInfo.NONE, true, false, true);
2768 assertEquals("Should return sd2, now resolved.", sd2, result);
2769 assertFalse("sd2 should now be resolved", sd2.isContinuation());
2770
2771 // Imported Packages
2772 LanguageLevelConverter.symbolTable.remove("fully.qualified.Woah");
2773 testLLVisitor.visitedFiles.clear();
2774 testLLVisitor._file = new File("testFiles/Fake.dj0");
2775 testLLVisitor._package = "";
2776 testLLVisitor._importedPackages.addLast("fully.qualified");
2777 sd2 = new SymbolData("fully.qualified.Woah");
2778 LanguageLevelConverter.symbolTable.put("fully.qualified.Woah", sd2);
2779 assertEquals("should find the unresolved symbol data in the symbol table", sd2,
2780 testLLVisitor.getQualifiedSymbolData("fully.qualified.Woah", SourceInfo.NONE, false, false, true));
2781 assertTrue("should not be a continuation", sd2.isContinuation());
2782
2783 sd2.setIsContinuation(false);
2784 result = testLLVisitor.getQualifiedSymbolData("fully.qualified.Woah", SourceInfo.NONE, true, false, true);
2785 assertEquals("should find the resolved symbol data in the symbol table", sd2, result);
2786 assertFalse("should not be a continuation", sd2.isContinuation());
2787
2788 //test java.lang classes that need to be looked up
2789 //want to resolve
2790 SymbolData stringSD = new SymbolData("java.lang.String");
2791 SymbolData newsd1 = testLLVisitor.getQualifiedSymbolData("java.lang.String", SourceInfo.NONE, true, true, true);
2792 assertEquals("should have correct name.", stringSD.getName(), newsd1.getName());
2793 assertFalse("should not be a continuation", newsd1.isContinuation());
2794
2795 // Test ambiguous class name (i.e. it is unqualified, and matches unqualified names in 2 or more packages.
2796 LanguageLevelConverter.symbolTable.put("random.package.String", new SymbolData("random.package.String"));
2797 // LanguageLevelConverter.symbolTable.put("java.lang.Object", new SymbolData("java.lang.Object"));
2798 testLLVisitor._importedPackages.addLast("random.package");
2799 result = testLLVisitor.getSymbolData("String", SourceInfo.NONE);
2800 assertEquals("Result should be null", null, result);
2801 assertEquals("There should be 1 error", 1, errors.size());
2802 assertEquals("The error message should be correct", "The class name String is ambiguous." +
2803 " It could be java.lang.String or random.package.String",
2804 errors.get(0).getFirst());
2805
2806 LanguageLevelConverter.symbolTable.remove("random.package.String");
2807
2808 }
2809
2810 public void test_forModifiersAndVisibility() {
2811 // Test access specifiers.
2812 testLLVisitor.forModifiersAndVisibility(PUBLIC_MAV);
2813 testLLVisitor.forModifiersAndVisibility(PROTECTED_MAV);
2814 testLLVisitor.forModifiersAndVisibility(PRIVATE_MAV);
2815 testLLVisitor.forModifiersAndVisibility(PACKAGE_MAV);
2816
2817
2818 assertEquals("There should be no errors.", 0, errors.size());
2819
2820 // Test "public", "private"
2821 ModifiersAndVisibility testMav =
2822 new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public", "private"});
2823 testLLVisitor.forModifiersAndVisibility(testMav);
2824 assertEquals("There should be one error.", 1, errors.size());
2825 assertEquals("The error message should be correct.", "Illegal combination of modifiers." +
2826 " Can't use private and public together.", errors.get(0).getFirst());
2827
2828 // Test "public", "abstract"
2829 testMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public", "abstract"});
2830 testLLVisitor.forModifiersAndVisibility(testMav);
2831 assertEquals("Still only one error.", 1, errors.size());
2832
2833 // Test "abstract", "final"
2834 testMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"abstract", "final"});
2835 testLLVisitor.forModifiersAndVisibility(testMav);
2836 assertEquals("There should be two errors.", 2, errors.size());
2837 assertEquals("The error message should be correct.", "Illegal combination of modifiers." +
2838 " Can't use final and abstract together.", errors.get(1).getFirst());
2839
2840 // Test "final", "abstract"
2841 testMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"final", "abstract"});
2842 testLLVisitor.forModifiersAndVisibility(testMav);
2843 assertEquals("There should still be two errors.", 2, errors.size()); // Generated error is duplicate
2844 assertEquals("The error message should be correct.", "Illegal combination of modifiers." +
2845 " Can't use final and abstract together.", errors.get(1).getFirst());
2846
2847 // Test "volatile", "final"
2848 testMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"volatile", "final"});
2849 testLLVisitor.forModifiersAndVisibility(testMav);
2850 assertEquals("There should be three errors.", 3, errors.size()); // Generated one new error
2851 assertEquals("The error message should be correct.", "Illegal combination of modifiers." +
2852 " Can't use final and volatile together.", errors.get(2).getFirst());
2853
2854 // Test "static", "final", "static"
2855 testMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"static", "final", "static"});
2856 testLLVisitor.forModifiersAndVisibility(testMav);
2857 assertEquals("There should be four errors.", 4, errors.size()); // Generated one new error
2858 assertEquals("The error message should be correct.", "Duplicate modifier: static", errors.get(3).getFirst());
2859 }
2860
2861 public void testGetQualifiedClassName() {
2862 //first test when the package is empty:
2863 testLLVisitor._package="";
2864 assertEquals("Should not change qualified name.", "simpson.Bart",
2865 testLLVisitor.getQualifiedClassName("simpson.Bart"));
2866 assertEquals("Should not change unqualified name.", "Lisa", testLLVisitor.getQualifiedClassName("Lisa"));
2867
2868 //now test when package is not empty.
2869 testLLVisitor._package="myPackage";
2870 assertEquals("Should not change properly packaged qualified name.", "myPackage.Snowball",
2871 testLLVisitor.getQualifiedClassName("myPackage.Snowball"));
2872 assertEquals("Should append package to front of not fully packaged name", "myPackage.simpson.Snowball",
2873 testLLVisitor.getQualifiedClassName("simpson.Snowball"));
2874 assertEquals("Should append package to front of unqualified class name.", "myPackage.Grandpa",
2875 testLLVisitor.getQualifiedClassName("Grandpa"));
2876 }
2877
2878 public void testAddSymbolData() {
2879 /**Put super class in symbol table.*/
2880 SymbolData obj = LanguageLevelConverter.symbolTable.get("java.lang.Object");
2881 // obj.setIsContinuation(false);
2882 // LanguageLevelConverter.symbolTable.put("java.lang.Object", obj);
2883
2884 ClassDef cd =
2885 new ClassDef(SourceInfo.NONE, PUBLIC_MAV, new Word(SourceInfo.NONE, "Awesome"),
2886 new TypeParameter[0],
2887 new ClassOrInterfaceType(SourceInfo.NONE, "java.lang.Object", new Type[0]),
2888 new ReferenceType[0],
2889 new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
2890
2891 SymbolData sd = new SymbolData("Awesome"); /**Create a continuation and store it in table.*/
2892 sd.setSuperClass(LanguageLevelConverter.symbolTable.get("java.lang.Object"));
2893 LanguageLevelConverter.symbolTable.put("Awesome", sd);
2894 SymbolData result = testLLVisitor.defineSymbolData(cd, "Awesome");
2895 assertFalse("result should not be a continuation.", result.isContinuation());
2896 assertFalse("sd should also no longer be a continuation.", sd.isContinuation());
2897 assertEquals("result and sd should be equal.", sd, result);
2898
2899 // /**Hierarchy should be empty at the end.*/
2900 // assertEquals("hierarchy should be empty", 0, _hierarchy.size());
2901
2902 /**Check that if the class is already defined, an appropriate error is thrown.*/
2903 assertEquals("Should return null, because it is already in the SymbolTable.", null,
2904 testLLVisitor.defineSymbolData(cd, "Awesome"));
2905 assertEquals("Length of errors should now be 1.", 1, errors.size());
2906 assertEquals("Error message should be correct.", "The class or interface Awesome has already been defined.",
2907 errors.get(0).getFirst());
2908 // assertEquals("hierarchy should be empty.", 0, _hierarchy.size());
2909
2910 }
2911
2912 public void test_variableDeclaration2VariableData() {
2913 VariableDeclarator[] d1 = {
2914 new UninitializedVariableDeclarator(SourceInfo.NONE,
2915 new PrimitiveType(SourceInfo.NONE, "int"),
2916 new Word(SourceInfo.NONE, "i")) };
2917 VariableDeclaration vd1 = new VariableDeclaration(SourceInfo.NONE,PUBLIC_MAV, d1);
2918 VariableData[] vdata1 = { new VariableData("i", PUBLIC_MAV, SymbolData.INT_TYPE, false, _sd1) };
2919
2920 assertTrue("Should properly recognize a basic VariableDeclaration",
2921 arrayEquals(vdata1, testLLVisitor._variableDeclaration2VariableData(vd1, _sd1)));
2922
2923 VariableDeclarator[] d2 = {
2924 new UninitializedVariableDeclarator(SourceInfo.NONE,
2925 new PrimitiveType(SourceInfo.NONE, "int"),
2926 new Word(SourceInfo.NONE, "i")),
2927 new InitializedVariableDeclarator(SourceInfo.NONE,
2928 new PrimitiveType(SourceInfo.NONE, "boolean"),
2929 new Word(SourceInfo.NONE, "b"),
2930 new BooleanLiteral(SourceInfo.NONE, true)) };
2931 VariableDeclaration vd2 = new VariableDeclaration(SourceInfo.NONE,PRIVATE_MAV, d2);
2932 VariableData bData = new VariableData("b", PRIVATE_MAV, SymbolData.BOOLEAN_TYPE, true, _sd1);
2933 bData.setHasInitializer(true);
2934 VariableData[] vdata2 = {new VariableData("i", PRIVATE_MAV, SymbolData.INT_TYPE, false, _sd1),
2935 bData};
2936
2937 assertTrue("Should properly recognize a more complicated VariableDeclaration",
2938 arrayEquals(vdata2, testLLVisitor._variableDeclaration2VariableData(vd2, _sd1)));
2939
2940 //check that if the type cannot be found, no error is thrown.
2941 VariableDeclarator[] d3 = {
2942 new UninitializedVariableDeclarator(SourceInfo.NONE,
2943 new ClassOrInterfaceType(SourceInfo.NONE, "LinkedList", new Type[0]),
2944 new Word(SourceInfo.NONE, "myList"))};
2945 VariableDeclaration vd3 = new VariableDeclaration(SourceInfo.NONE, PRIVATE_MAV, d3);
2946 testLLVisitor._variableDeclaration2VariableData(vd3, _sd1);
2947 assertEquals("There should now be no errors", 0, errors.size());
2948 // assertEquals("The error message should be correct", "Class or Interface LinkedList not found",
2949 // errors.get(0).getFirst());
2950
2951 }
2952
2953 public void test_addError() {
2954 LinkedList<Pair<String, JExpressionIF>> e = new LinkedList<Pair<String, JExpressionIF>>();
2955
2956 NullLiteral nl = new NullLiteral(SourceInfo.NONE);
2957 NullLiteral nl2 = new NullLiteral(SourceInfo.NONE);
2958
2959 e.addLast(new Pair<String,JExpressionIF>("Boy, is this an error!", nl));
2960 _addError("Boy, is this an error!", nl);
2961
2962 assertTrue("An error should have been added.", _errorAdded);
2963 assertEquals("The errors list should be correct.", e, errors);
2964
2965 e.addLast(new Pair<String,JExpressionIF>("Error again!", nl2));
2966 _addError("Error again!", nl2);
2967
2968 assertTrue("Another error should have been aded.", _errorAdded);
2969 assertEquals("The new errors list should be correct.", e, errors);
2970 }
2971
2972 public void test_addAndIgnoreError() {
2973 LinkedList<Pair<String, JExpressionIF>> e = new LinkedList<Pair<String, JExpressionIF>>();
2974
2975 NullLiteral nl = new NullLiteral(SourceInfo.NONE);
2976 NullLiteral nl2 = new NullLiteral(SourceInfo.NONE);
2977
2978 _errorAdded = false;
2979
2980 e.addLast(new Pair<String,JExpressionIF>("Nobody pays attention to me!", nl));
2981 _addAndIgnoreError("Nobody pays attention to me!", nl);
2982
2983 assertFalse("_errorAdded should be false.", _errorAdded);
2984 assertEquals("The errors list should be correct.", e, errors);
2985
2986 e.addLast(new Pair<String,JExpressionIF>("Cellophane, I'm Mr. Cellophane", nl2));
2987 _addAndIgnoreError("Cellophane, I'm Mr. Cellophane", nl2);
2988
2989 assertFalse("errorAdded should still be false.", _errorAdded);
2990 assertEquals("The new errors list should be correct.", e, errors);
2991
2992 _errorAdded = true;
2993 try {
2994 _addAndIgnoreError("This should throw an exception, because _errorAdded is true.", nl);
2995 assertTrue("An error should have been thrown!", false);
2996 }
2997 catch (RuntimeException exc) {
2998 assertEquals("Make sure runtime exception message is correct.",
2999 "Internal Program Error: _addAndIgnoreError called while _errorAdded was true." +
3000 " Please report this bug.",
3001 exc.getMessage());
3002 }
3003 _errorAdded = false;
3004 }
3005
3006 public void test_checkError() {
3007 _errorAdded = false;
3008 assertFalse("_checkError should return false", _checkError());
3009
3010 _errorAdded = true;
3011 assertTrue("_checkError should return true", _checkError());
3012 assertFalse("_checkError should have set _errorAdded to false.", _errorAdded);
3013 }
3014
3015 public void testForClassDefDoFirst() {
3016 ClassDef cd =
3017 new ClassDef(SourceInfo.NONE, PUBLIC_MAV,
3018 new Word(SourceInfo.NONE, "Awesome"),
3019 new TypeParameter[0],
3020 new ClassOrInterfaceType(SourceInfo.NONE, "java.lang.Object", new Type[0]),
3021 new ReferenceType[0],
3022 new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
3023 testLLVisitor.forClassDefDoFirst(cd);
3024 assertEquals("There should be no errors.", 0, errors.size());
3025 testLLVisitor._importedFiles.addLast(new File("Awesome").getAbsolutePath());
3026 testLLVisitor.forClassDefDoFirst(cd);
3027 assertEquals("There should be one error.", 1, errors.size());
3028 assertEquals("The error message should be correct.", "The class Awesome was already imported.",
3029 errors.get(0).getFirst());
3030
3031 ClassDef cd2 = new ClassDef(SourceInfo.NONE, PRIVATE_MAV,
3032 new Word(SourceInfo.NONE, "privateClass"),
3033 new TypeParameter[0],
3034 new ClassOrInterfaceType(SourceInfo.NONE, "java.lang.Object", new Type[0]),
3035 new ReferenceType[0],
3036 new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
3037 testLLVisitor.forClassDefDoFirst(cd2);
3038 assertEquals("There should be 2 errors", 2, errors.size());
3039 assertEquals("The 2nd error message should be correct", "Top level classes cannot be private",
3040 errors.get(1).getFirst());
3041
3042 }
3043
3044 public void testForInterfaceDefDoFirst() {
3045 InterfaceDef id = new InterfaceDef(SourceInfo.NONE, PUBLIC_MAV,
3046 new Word(SourceInfo.NONE, "Awesome"),
3047 new TypeParameter[0], new ReferenceType[0],
3048 new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
3049 testLLVisitor.forInterfaceDefDoFirst(id);
3050 assertEquals("There should be no errors.", 0, errors.size());
3051
3052 InterfaceDef id2 = new InterfaceDef(SourceInfo.NONE, PRIVATE_MAV,
3053 new Word(SourceInfo.NONE, "privateinterface"),
3054 new TypeParameter[0], new ReferenceType[0],
3055 new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
3056 testLLVisitor.forInterfaceDefDoFirst(id2);
3057 assertEquals("There should be 1 errors", 1, errors.size());
3058 assertEquals("The error message should be correct", "Top level interfaces cannot be private",
3059 errors.get(0).getFirst());
3060
3061 InterfaceDef id3 = new InterfaceDef(SourceInfo.NONE, FINAL_MAV,
3062 new Word(SourceInfo.NONE, "finalinterface"),
3063 new TypeParameter[0], new ReferenceType[0],
3064 new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
3065 testLLVisitor.forInterfaceDefDoFirst(id3);
3066 assertEquals("There should be 2 errors", 2, errors.size());
3067 assertEquals("The error message should be correct", "Interfaces cannot be final", errors.get(1).getFirst());
3068 }
3069
3070 public void testForInnerInterfaceDefDoFirst() {
3071 InterfaceDef id = new InterfaceDef(SourceInfo.NONE, PUBLIC_MAV,
3072 new Word(SourceInfo.NONE, "Awesome"),
3073 new TypeParameter[0], new ReferenceType[0],
3074 new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
3075 id.visit(testLLVisitor);
3076 assertEquals("There should be no errors.", 0, errors.size());
3077
3078 InnerInterfaceDef id2 =
3079 new InnerInterfaceDef(SourceInfo.NONE, FINAL_MAV, new Word(SourceInfo.NONE, "finalinterface"),
3080 new TypeParameter[0], new ReferenceType[0],
3081 new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
3082 id2.visit(testLLVisitor);
3083 assertEquals("There should be 1 error", 1, errors.size());
3084 assertEquals("The error message should be correct", "Interfaces cannot be final", errors.get(0).getFirst());
3085 }
3086
3087 public void testForPackageStatementOnly() {
3088 Word[] words = new Word[] {new Word(SourceInfo.NONE, "alpha"),
3089 new Word(SourceInfo.NONE, "beta")};
3090 CompoundWord cw = new CompoundWord(SourceInfo.NONE, words);
3091 PackageStatement ps = new PackageStatement(SourceInfo.NONE, cw);
3092 testLLVisitor._file = new File("alpha/beta/delta");
3093 testLLVisitor.forPackageStatementOnly(ps);
3094 assertEquals("_package should be set correctly.", "alpha.beta", testLLVisitor._package);
3095 assertEquals("There should be no errors.", 0, errors.size());
3096 testLLVisitor._file = new File("alpha/beta/beta/delta");
3097 testLLVisitor.forPackageStatementOnly(ps);
3098 assertEquals("_package should be set correctly.", "alpha.beta", testLLVisitor._package);
3099 assertEquals("There should be one error.", 1, errors.size());
3100 assertEquals("The error message should be correct.", "The package name must mirror your file's directory.",
3101 errors.get(0).getFirst());
3102 }
3103
3104 public void testForClassImportStatementOnly() {
3105
3106 //Test one that works
3107 Word[] words = new Word[] { new Word(SourceInfo.NONE, "alpha"), new Word(SourceInfo.NONE, "beta")};
3108 CompoundWord cw = new CompoundWord(SourceInfo.NONE, words);
3109 ClassImportStatement cis = new ClassImportStatement(SourceInfo.NONE, cw);
3110 SymbolData sd = new SymbolData("alpha.beta");
3111 testLLVisitor.forClassImportStatementOnly(cis);
3112 assertTrue("imported files should contain alpha.beta", testLLVisitor._importedFiles.contains("alpha.beta"));
3113 // continuations should not appear in symbolTable
3114 // assertEquals("There should be a continuation.", sd, LanguageLevelConverter.symbolTable.get("alpha.beta"));
3115 assertTrue("It should be in continuations.", testLLVisitor.continuations.containsKey("alpha.beta"));
3116
3117 // Test one that should throw an error: Class has already been imported. alpha.beta should now be in the
3118 // symbolTable, and alpha should be in the list of packages, so this will throw an error
3119 Word[] words2 = new Word[] { new Word(SourceInfo.NONE, "gamma"), new Word(SourceInfo.NONE, "beta")};
3120 CompoundWord cw2 = new CompoundWord(SourceInfo.NONE, words2);
3121 ClassImportStatement cis2 = new ClassImportStatement(SourceInfo.NONE, cw2);
3122 cis2.visit(testLLVisitor);
3123
3124 assertEquals("There should be one error", 1, errors.size());
3125 assertEquals("The error message should be correct",
3126 "The class beta has already been imported.",
3127 errors.get(0).getFirst());
3128
3129 // Consistent with javac, the following test no longer throws an error.
3130 // //Test one that should throw an error: Importing a class from the current package
3131 // testLLVisitor._package = "myPackage";
3132 // Word[] words3 = new Word[] { new Word(SourceInfo.NONE, "myPackage"), new Word(SourceInfo.NONE, "cookie")};
3133 // CompoundWord cw3 = new CompoundWord(SourceInfo.NONE, words3);
3134 // ClassImportStatement cis3 = new ClassImportStatement(SourceInfo.NONE, cw3);
3135 // cis3.visit(testLLVisitor);
3136 //
3137 // assertEquals("There should now be 2 errors", 2, errors.size());
3138 // assertEquals("The second error message should be correct",
3139 // "You do not need to import myPackage.cookie. It is in your package so it is already visible",
3140 // errors.get(1).getFirst());
3141 }
3142
3143 public void testForPackageImportStatementOnly() {
3144 //Test one that works
3145 Word[] words = new Word[] {new Word(SourceInfo.NONE, "alpha"),
3146 new Word(SourceInfo.NONE, "beta")};
3147 CompoundWord cw = new CompoundWord(SourceInfo.NONE, words);
3148 PackageImportStatement cis = new PackageImportStatement(SourceInfo.NONE, cw);
3149 SymbolData sd = new SymbolData("alpha.beta");
3150 testLLVisitor.forPackageImportStatementOnly(cis);
3151 assertEquals("There should be no errorrs", 0, errors.size());
3152 assertTrue("Imported Packages should now contain alpha.beta",
3153 testLLVisitor._importedPackages.contains("alpha.beta"));
3154
3155 //Test one that should not throw an error: Importing a subpackage of the current package
3156 testLLVisitor._package = "myPackage";
3157 Word[] words3 = new Word[] {new Word(SourceInfo.NONE, "myPackage"), new Word(SourceInfo.NONE,
3158 "cookie")};
3159 CompoundWord cw3 = new CompoundWord(SourceInfo.NONE, words3);
3160 PackageImportStatement pis = new PackageImportStatement(SourceInfo.NONE, cw3);
3161 pis.visit(testLLVisitor);
3162
3163 assertEquals("There should be no errors", 0, errors.size());
3164 assertTrue("Imported Packages should now contain myPackage.cookie",
3165 testLLVisitor._importedPackages.contains("myPackage.cookie"));
3166
3167
3168
3169 //Test one that should throw an error: Importing the current package
3170 Word[] words2 = new Word[] {new Word(SourceInfo.NONE, "myPackage")};
3171 CompoundWord cw2 = new CompoundWord(SourceInfo.NONE, words2);
3172 PackageImportStatement pis2 = new PackageImportStatement(SourceInfo.NONE, cw2);
3173 pis2.visit(testLLVisitor);
3174
3175 assertEquals("There should now be 1 errors", 1, errors.size());
3176 assertEquals("The error message should be correct", "You do not need to import package myPackage." +
3177 " It is your package so all public classes in it are already visible.", errors.get(0).getFirst());
3178
3179 }
3180
3181 public void testForSourceFile() {
3182 ClassDef cd = new ClassDef(SourceInfo.NONE, PUBLIC_MAV, new Word(SourceInfo.NONE, "Awesome"),
3183 new TypeParameter[0],
3184 new ClassOrInterfaceType(SourceInfo.NONE, "java.lang.Object", new Type[0]),
3185 new ReferenceType[0],
3186 new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
3187 ClassDef cd2 = new ClassDef(SourceInfo.NONE, PUBLIC_MAV, new Word(SourceInfo.NONE, "Gnarly"),
3188 new TypeParameter[0],
3189 new ClassOrInterfaceType(SourceInfo.NONE, "Awesome", new Type[0]),
3190 new ReferenceType[0],
3191 new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
3192 InterfaceDef id = new InterfaceDef(SourceInfo.NONE, PUBLIC_MAV,
3193 new Word(SourceInfo.NONE, "NiftyWords"),
3194 new TypeParameter[0],
3195 new ReferenceType[0],
3196 new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
3197
3198 SourceFile sf = new SourceFile(SourceInfo.NONE,
3199 new PackageStatement[0],
3200 new ImportStatement[0],
3201 new TypeDefBase[] {cd, cd2, id});
3202 testLLVisitor.forSourceFile(sf);
3203
3204 assertTrue("_classesInThisFile should contain the two ClassDefs.",
3205 testLLVisitor._classesInThisFile.contains("Awesome"));
3206 assertTrue("_classesInThisFile should contain the two ClassDefs.",
3207 testLLVisitor._classesInThisFile.contains("Gnarly"));
3208
3209 assertTrue("_classesInThisFile should contain the InterfaceDef",
3210 testLLVisitor._classesInThisFile.contains("NiftyWords"));
3211 assertTrue("_classesInThisFile should contain the two ClassDefs.",
3212 testLLVisitor._classesInThisFile.contains("Awesome"));
3213 assertTrue("_classesInThisFile should contain the two ClassDefs.",
3214 testLLVisitor._classesInThisFile.contains("Gnarly"));
3215 assertTrue("_classesInThisFile should contain the InterfaceDef",
3216 testLLVisitor._classesInThisFile.contains("NiftyWords"));
3217
3218 }
3219
3220 public void testReferenceType2String() {
3221 // Try a TypeVariable
3222 TypeVariable tv = new TypeVariable(SourceInfo.NONE, "T");
3223 String[] result = testLLVisitor.referenceType2String(new ReferenceType[] { tv });
3224 assertEquals("There should not be any errors.", 0, errors.size());
3225 assertEquals("Results should have one String.", 1, result.length);
3226 assertEquals("The String should be \"T\".", "T", result[0]);
3227
3228 // Try a ClassOrInterfaceType
3229 ClassOrInterfaceType coit = new ClassOrInterfaceType(SourceInfo.NONE,
3230 "MyClass",
3231 new Type[] { new TypeVariable(SourceInfo.NONE, "T"),
3232 new TypeVariable(SourceInfo.NONE, "U")}
3233 );
3234 result = testLLVisitor.referenceType2String(new ReferenceType[] { tv, coit });
3235 assertEquals("There should not be any errors.", 0, errors.size());
3236 assertEquals("Results should have two Strings.", 2, result.length);
3237 assertEquals("The first String should be \"T\".", "T", result[0]);
3238 assertEquals("The second String should be \"MyClass\".", "MyClass", result[1]);
3239
3240 // Try a MemberType
3241 MemberType mt = new MemberType(SourceInfo.NONE,
3242 "MyClass.MyClass2",
3243 coit,
3244 new ClassOrInterfaceType(SourceInfo.NONE,
3245 "MyClass2",
3246 new Type[0]));
3247 result = testLLVisitor.referenceType2String(new ReferenceType[] { mt });
3248 assertEquals("There should not be any errors.", 0, errors.size());
3249 assertEquals("Results should have one String.", 1, result.length);
3250 assertEquals("The first String should be \"MyClass.MyClass2\".", "MyClass.MyClass2", result[0]);
3251 }
3252
3253
3254 public void testExceptionsInSymbolTable() {
3255
3256 // Make sure that exceptions are being added to symbol table
3257 ClassOrInterfaceType exceptionType =
3258 new ClassOrInterfaceType(SourceInfo.NONE, "java.util.prefs.BackingStoreException", new Type[0]);
3259 ParenthesizedExpressionList expList = new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[0]);
3260
3261 BracedBody bb =
3262 new BracedBody(SourceInfo.NONE,
3263 new BodyItemI[] { new ThrowStatement(SourceInfo.NONE,
3264 new SimpleNamedClassInstantiation(SourceInfo.NONE,
3265 exceptionType,
3266 expList))});
3267 bb.visit(testLLVisitor);
3268 assertNotNull("The SymbolTable should have java.util.prefs.BackingStoreException",
3269 LanguageLevelConverter.symbolTable.get("java.util.prefs.BackingStoreException"));
3270
3271 }
3272
3273 public void testShouldBreak() {
3274 //shift assignment expressions:
3275 LeftShiftAssignmentExpression shift1 =
3276 new LeftShiftAssignmentExpression(SourceInfo.NONE, new NullLiteral(SourceInfo.NONE),
3277 new NullLiteral(SourceInfo.NONE));
3278 RightUnsignedShiftAssignmentExpression shift2 =
3279 new RightUnsignedShiftAssignmentExpression(SourceInfo.NONE,
3280 new NullLiteral(SourceInfo.NONE),
3281 new NullLiteral(SourceInfo.NONE));
3282 RightSignedShiftAssignmentExpression shift3 =
3283 new RightSignedShiftAssignmentExpression(SourceInfo.NONE,
3284 new NullLiteral(SourceInfo.NONE),
3285 new NullLiteral(SourceInfo.NONE));
3286
3287
3288 shift1.visit(testLLVisitor);
3289 assertEquals("Should be no errors", 0, errors.size());
3290 // assertEquals("error message should be correct",
3291 // "Shift assignment operators cannot be used at any language level",
3292 // errors.getLast().getFirst());
3293
3294 shift2.visit(testLLVisitor);
3295 assertEquals("Should be no errors", 0, errors.size());
3296 // assertEquals("error message should be correct",
3297 // "Shift assignment operators cannot be used at any language level",
3298 // errors.getLast().getFirst());
3299
3300 shift3.visit(testLLVisitor);
3301 assertEquals("Should be no errors", 0, errors.size());
3302 // assertEquals("error message should be correct",
3303 // "Shift assignment operators cannot be used at any language level",
3304 // errors.getLast().getFirst());
3305
3306 //BitwiseAssignmentExpressions
3307 BitwiseAndAssignmentExpression bit1 =
3308 new BitwiseAndAssignmentExpression(SourceInfo.NONE, new NullLiteral(SourceInfo.NONE),
3309 new NullLiteral(SourceInfo.NONE));
3310 BitwiseOrAssignmentExpression bit2 =
3311 new BitwiseOrAssignmentExpression(SourceInfo.NONE, new NullLiteral(SourceInfo.NONE),
3312 new NullLiteral(SourceInfo.NONE));
3313 BitwiseXorAssignmentExpression bit3 =
3314 new BitwiseXorAssignmentExpression(SourceInfo.NONE, new NullLiteral(SourceInfo.NONE),
3315 new NullLiteral(SourceInfo.NONE));
3316
3317 bit1.visit(testLLVisitor);
3318 assertEquals("Should be no errors", 0, errors.size());
3319 // assertEquals("error message should be correct", "Bitwise operators cannot be used at any language level",
3320 // errors.getLast().getFirst());
3321
3322 bit2.visit(testLLVisitor);
3323 assertEquals("Should be no errors", 0, errors.size());
3324 // assertEquals("error message should be correct", "Bitwise operators cannot be used at any language level",
3325 // errors.getLast().getFirst());
3326
3327 bit3.visit(testLLVisitor);
3328 assertEquals("Should be no errors", 0, errors.size());
3329 // assertEquals("error message should be correct", "Bitwise operators cannot be used at any language level",
3330 // errors.getLast().getFirst());
3331
3332 //BitwiseExpressions
3333 BitwiseAndExpression bit4 =
3334 new BitwiseAndExpression(SourceInfo.NONE, new NullLiteral(SourceInfo.NONE),
3335 new NullLiteral(SourceInfo.NONE));
3336 BitwiseOrExpression bit5 =
3337 new BitwiseOrExpression(SourceInfo.NONE, new NullLiteral(SourceInfo.NONE),
3338 new NullLiteral(SourceInfo.NONE));
3339 BitwiseXorExpression bit6 =
3340 new BitwiseXorExpression(SourceInfo.NONE, new NullLiteral(SourceInfo.NONE),
3341 new NullLiteral(SourceInfo.NONE));
3342 BitwiseNotExpression bit7 =
3343 new BitwiseNotExpression(SourceInfo.NONE, new NullLiteral(SourceInfo.NONE));
3344
3345
3346 bit4.visit(testLLVisitor);
3347 assertEquals("Should be no errors", 0, errors.size());
3348 // assertEquals("error message should be correct",
3349 // "Bitwise and expressions cannot be used at any language level." +
3350 // " Perhaps you meant to compare two values using regular and (&&)",
3351 // errors.getLast().getFirst());
3352
3353 bit5.visit(testLLVisitor);
3354 assertEquals("Should be no errors", 0, errors.size());
3355 // assertEquals("error message should be correct",
3356 // "Bitwise or expressions cannot be used in the functional level." +
3357 // " Perhaps you meant to compare two values using regular or (||)",
3358 // errors.getLast().getFirst());
3359
3360 bit6.visit(testLLVisitor);
3361 assertEquals("Should be no errors", 0, errors.size());
3362 // assertEquals("error message should be correct", "Bitwise xor expressions cannot be used at any language level",
3363 // errors.getLast().getFirst());
3364
3365 bit7.visit(testLLVisitor);
3366 assertEquals("Should be no errors", 0, errors.size());
3367 // assertEquals("error message should be correct",
3368 // "Bitwise not expressions cannot be used at any language level." +
3369 // " Perhaps you meant to negate this value using regular not (!)",
3370 // errors.getLast().getFirst());
3371
3372 //shift binary expressions
3373 LeftShiftExpression shift4 =
3374 new LeftShiftExpression(SourceInfo.NONE, new NullLiteral(SourceInfo.NONE),
3375 new NullLiteral(SourceInfo.NONE));
3376 RightUnsignedShiftExpression shift5 =
3377 new RightUnsignedShiftExpression(SourceInfo.NONE, new NullLiteral(SourceInfo.NONE),
3378 new NullLiteral(SourceInfo.NONE));
3379 RightSignedShiftExpression shift6 =
3380 new RightSignedShiftExpression(SourceInfo.NONE, new NullLiteral(SourceInfo.NONE),
3381 new NullLiteral(SourceInfo.NONE));
3382
3383 shift4.visit(testLLVisitor);
3384 assertEquals("Should be no errors", 0, errors.size());
3385 // assertEquals("error message should be correct", "Bit shifting operators cannot be used at any language level",
3386 // errors.getLast().getFirst());
3387
3388 shift5.visit(testLLVisitor);
3389 assertEquals("Should be no errors", 0, errors.size());
3390 // assertEquals("error message should be correct", "Bit shifting operators cannot be used at any language level",
3391 // errors.getLast().getFirst());
3392
3393 shift6.visit(testLLVisitor);
3394 assertEquals("Should be no errors", 0, errors.size());
3395 // assertEquals("error message should be correct", "Bit shifting operators cannot be used at any language level",
3396 // errors.getLast().getFirst());
3397
3398 //empty expression
3399 EmptyExpression e = new EmptyExpression(SourceInfo.NONE);
3400 e.visit(testLLVisitor);
3401 assertEquals("Should be 1 error", 1, errors.size());
3402 assertEquals("Error message should be correct", "You appear to be missing an expression here",
3403 errors.getLast().getFirst());
3404
3405 //noop expression
3406 NoOpExpression noop =
3407 new NoOpExpression(SourceInfo.NONE, new NullLiteral(SourceInfo.NONE),
3408 new NullLiteral(SourceInfo.NONE));
3409 noop.visit(testLLVisitor);
3410 assertEquals("Should be 2 errors", 2, errors.size());
3411 assertEquals("Error message should be correct", "You are missing a binary operator here",
3412 errors.getLast().getFirst());
3413 }
3414
3415 public void testIsConstructor() {
3416 MethodData constr =
3417 new MethodData("monkey", PUBLIC_MAV, new TypeParameter[0], _sd1, new VariableData[0], new String[0], _sd1,
3418 new NullLiteral(SourceInfo.NONE));
3419 MethodData notRightOuter =
3420 new MethodData("monkey", PUBLIC_MAV, new TypeParameter[0], _sd1, new VariableData[0], new String[0], _sd2,
3421 new NullLiteral(SourceInfo.NONE));
3422 _sd2.setOuterData(_sd1);
3423 _sd1.addInnerClass(_sd2);
3424 MethodData notRightName =
3425 new MethodData("chimp", PUBLIC_MAV, new TypeParameter[0], _sd1, new VariableData[0], new String[0], _sd1,
3426 new NullLiteral(SourceInfo.NONE));
3427 MethodData notRightReturnType =
3428 new MethodData("monkey", PUBLIC_MAV, new TypeParameter[0], _sd2, new VariableData[0], new String[0], _sd1,
3429 new NullLiteral(SourceInfo.NONE));
3430
3431 //try one that works
3432 assertTrue(testLLVisitor.isConstructor(constr));
3433
3434 //wrong outer
3435 assertFalse(testLLVisitor.isConstructor(notRightOuter));
3436
3437 //wrong name
3438 assertFalse(testLLVisitor.isConstructor(notRightName));
3439
3440 //wrong return type
3441 assertFalse(testLLVisitor.isConstructor(notRightReturnType));
3442
3443 //not a method data
3444 assertFalse(testLLVisitor.isConstructor(_sd1));
3445 }
3446 }
3447 }