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 edu.rice.cs.javalanglevels.tree.*;
040 import edu.rice.cs.javalanglevels.parser.JExprParser;
041 import edu.rice.cs.javalanglevels.util.Log;
042 import edu.rice.cs.javalanglevels.util.UnexpectedException;
043 import edu.rice.cs.javalanglevels.util.Utilities;
044 import java.util.*;
045 import java.io.File;
046 import edu.rice.cs.plt.reflect.JavaVersion;
047 import edu.rice.cs.plt.iter.*;
048
049 import junit.framework.TestCase;
050
051 /** Does Type Checking that is not dependent on the enclosing body. Also does top level type checking. Common
052 * to all langauge levels. */
053 public class TypeChecker extends JExpressionIFDepthFirstVisitor<TypeData> implements JExpressionIFVisitor<TypeData> {
054
055 public static final SourceInfo NONE = SourceInfo.NONE;
056 public static final NullLiteral NULL_LITERAL = new NullLiteral(NONE);
057
058 public static final ModifiersAndVisibility _packageMav = new ModifiersAndVisibility(NONE, new String[0]);
059 public static final ModifiersAndVisibility _publicMav = new ModifiersAndVisibility(NONE, new String[] {"public"});
060 public static final ModifiersAndVisibility _protectedMav =
061 new ModifiersAndVisibility(NONE, new String[] {"protected"});
062 public static final ModifiersAndVisibility _privateMav = new ModifiersAndVisibility(NONE, new String[] {"private"});
063
064 public static final ModifiersAndVisibility _abstractMav = new ModifiersAndVisibility(NONE, new String[] {"abstract"});
065 public static final ModifiersAndVisibility _finalMav = new ModifiersAndVisibility(NONE, new String[] {"final"});
066 public static final ModifiersAndVisibility _finalPublicMav =
067 new ModifiersAndVisibility(NONE, new String[] {"final", "public"});
068 public static final ModifiersAndVisibility _publicAbstractMav =
069 new ModifiersAndVisibility(NONE, new String[] {"public", "abstract"});
070 public static final ModifiersAndVisibility _publicStaticMav =
071 new ModifiersAndVisibility(NONE, new String[] {"public", "static"});
072
073 protected static final Log _log = new Log("LLConverter.txt", false);
074
075 /** Holds any errors that are encountered during TypeChecking */
076 static LinkedList<Pair<String, JExpressionIF>> errors;
077
078 /** Holds the information about any classes/interfaces that have been resolved */
079 static final Symboltable symbolTable = LanguageLevelConverter.symbolTable;
080
081 /**True if we have an error we can't recover from*/
082 static boolean _errorAdded;
083
084 /** The source file we are type checking */
085 File _file;
086
087 /** The package of the source file. */
088 String _package;
089
090 /** Holds the names of the specifically imported classes. */
091 LinkedList<String> _importedFiles;
092
093 /**Holds the names of packages that are imported in the source file*/
094 LinkedList<String> _importedPackages;
095
096 /** The normal constructor. Called to begin type checking
097 * @param file The source file being type checked
098 * @param packageName The name of the package of the source file
099 * @param errors The list of errors that are encountered during type checking
100 * @param importedFiles The specific classes imported in the source file
101 * @param importedPackages The list of package names that are imported
102 */
103 public TypeChecker(File file, String packageName, LinkedList<Pair<String, JExpressionIF>> errors,
104 Symboltable symbolTable, LinkedList<String> importedFiles,
105 LinkedList<String> importedPackages) {
106 _file = file;
107 _package = packageName;
108 this.errors = errors;
109 // this.symbolTable = symbolTable;
110 this._importedFiles = importedFiles;
111 this._importedPackages = importedPackages;
112 }
113
114 /** Called by the subclasses.
115 * @param file The Source File being checked
116 * @param packageName The package the source file is in
117 * @param importedFiles The specific classes imported in the source file
118 * @param importedPackages The list of package names that are imported
119 */
120 public TypeChecker(File file, String packageName, LinkedList<String> importedFiles,
121 LinkedList<String> importedPackages) {
122 _file = file;
123 _package = packageName;
124 _importedFiles = importedFiles;
125 _importedPackages = importedPackages;
126 }
127
128 /**The top level type checker does not have a data */
129 protected Data _getData() {
130 throw new RuntimeException("Internal Program Error: _getData() shouldn't get called from TypeChecker. " +
131 "Please report this bug.");
132 }
133
134 /** Adds an appropriate definition of junit.framework.TestCase to symbolTable. */
135 protected static SymbolData defineTestCaseClass() {
136 SymbolData testCase = new SymbolData("junit.framework.TestCase");
137 testCase.setIsContinuation(false);
138 testCase.setMav(_publicMav);
139 testCase.setPackage("junit.framework");
140 LanguageLevelConverter.symbolTable.put("junit.framework.TestCase", testCase);
141 return testCase;
142 }
143
144 /** Returns the SymbolData corresponding to the name className. Checks 1) unqualified or partially-qualified inner
145 * classes visible in this scope, 2) fully-qualified inner classes, 3) primitives, 4) array types, 5) fully
146 * qualified top-level classes, 6) imported classes, 7) a top-level class within this package, 8) imported packages,
147 * and 9) java.lang classes. Assumes that an error should be generated if the class is not found, and that
148 * classes are not allowed to extend java.lang.Runnable.
149 * Will always check accessiblity since giveException is assumed to be true.
150 * @param className The class name to look up which may or may not be fully qualified.
151 * @param enclosingData The enclosing data -- either a MethodData or a ClassData for this reference
152 * @param jexpr The AST of the phrase containing the reference.
153 * */
154 public SymbolData getSymbolData(String className, Data enclosingData, JExpression jexpr) {
155 return getSymbolData(className, enclosingData, jexpr, true);
156 }
157
158 /** Call the next version of GetSymbolData, but pass it giveException as the flag for whether or not to
159 * give ambigException.
160 */
161 public SymbolData getSymbolData(String className, Data enclosingData, JExpression jexpr, boolean giveException) {
162 return getSymbolData(giveException, className, enclosingData, jexpr, giveException);
163 }
164
165 /** Returns the SymbolData corresponding to the name className. Checks 1) unqualified or partially-qualified inner
166 * classes visible in this scope, 2) fully-qualified inner classes, 3) primitives, 4) array types, 5) fully
167 * qualified top-level classes, 6) imported classes, 7) a top-level class within this package, 8) imported packages,
168 * and 9) java.lang classes. Assumes that classes are not allowed to extend java.lang.Runnable.
169 * Will only check accessibility if giveException is true.
170 * */
171 public SymbolData getSymbolData(boolean giveAmbigException, String className, Data enclosingData, JExpression jexpr,
172 boolean giveException) {
173 Data d = enclosingData;
174 SymbolData result = null;
175 while (d != null && result == null) {
176 result = enclosingData.getInnerClassOrInterface(className);
177 d = d.getOuterData();
178 }
179
180 if (result == null) result = getSymbolData(className, jexpr, giveException, true);
181
182 else if (result == SymbolData.AMBIGUOUS_REFERENCE) {
183 if (giveAmbigException) {
184 _addError("Ambiguous reference to class or interface " + className, jexpr);
185 return SymbolData.AMBIGUOUS_REFERENCE;
186 }
187 return null; // return null to indicate we were unable to resolve this.
188 }
189 if (result == null || ! giveException) return result;
190 if (checkAccess(jexpr, result.getMav(), className, result, enclosingData.getSymbolData(), "class or interface")) {
191 return result;
192 }
193 else return result;
194
195 }
196
197 /** Returns the SymbolData corresponding to the name className, assuming that className
198 * does not refer to an unqualified or partially-qualified inner class.
199 * */
200 public SymbolData getSymbolData(String className, JExpression jexpr, boolean giveException, boolean runnableNotOkay) {
201 // Check qualified class name (which is no different at elementary level)
202 SourceInfo si = jexpr.getSourceInfo();
203
204 // Create a dummy LLV; this seems awkward. TODO: refactor
205 LanguageLevelVisitor llv =
206 new LanguageLevelVisitor(_file,
207 _package,
208 null, // enclosing class for top level traversal
209 _importedFiles,
210 _importedPackages,
211 new HashSet<String>(),
212 new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
213 new LinkedList<Command>(),
214 new HashMap<String, SymbolData>());
215
216 LanguageLevelConverter._newSDs.clear();
217 assert LanguageLevelConverter.symbolTable.containsKey("java.lang.Object");
218 SymbolData sd = llv.getSymbolData(className, si, false, true); // TODO: Is this right?
219 // if (sd == null) {
220 // System.err.println("***ALARM*** The following symbol was not found in symbolTable: " + className);
221 // }
222 // else if (sd.getName().equals("java.lang.Throwable"))
223 // System.err.println("*** Package for retrieved java.lang.Throwable is " + sd.getPackage());
224 if (sd == null || sd.isContinuation()) {
225 if (giveException) { _addError("Class or variable " + className + " not found.", jexpr); }
226 return null;
227 }
228 else {
229 //Check to see that sd is really in the package it should be in.
230 if (notRightPackage(sd)) {
231 _addError("The class " + sd.getName() + " is not in the right package. Perhaps you meant to package it?",
232 jexpr);
233 }
234
235 if (runnableNotOkay) {
236 if (sd.implementsRunnable()) {
237 _addError(sd.getName() + " implements the Runnable interface, which is not allowed at any language level",
238 jexpr);
239 return null;
240 }
241 }
242 return sd;
243 }
244 }
245
246 /** Return false if the symbolData is in the wrong package.
247 * @param sd The SymbolData to check.
248 */
249 protected boolean notRightPackage(SymbolData sd) {
250 if (sd.getOuterData() != null) { // if this is an inner class, check its outer data.
251 return notRightPackage(sd.getOuterData().getSymbolData());
252 }
253 return (sd.getPackage().equals("") && sd.getName().lastIndexOf('.') != -1) ||
254 ! sd.getName().startsWith(sd.getPackage());
255 }
256
257 /** Create a clone of the linked list of Variable Data. */
258 public LinkedList<VariableData> cloneVariableDataList(LinkedList<VariableData> vars) {
259 LinkedList<VariableData> nv = new LinkedList<VariableData>();
260 for (int i = 0; i<vars.size(); i++) {
261 VariableData old = vars.get(i);
262 nv.addLast(old);
263 }
264 return nv;
265 }
266
267 /** The Qualified Class Name is the package, followed by a dot, followed by the rest of the class name.
268 * If the provided className is already qualified, just return it. If the package is not empty,
269 * and the className does not start with the package, append the package name onto the className, and return it.
270 * @param className The className to qualify.
271 */
272 protected String getQualifiedClassName(String className) {
273 if (! _package.equals("") && ! className.startsWith(_package)) { return _package + '.' + className;}
274 else return className;
275 }
276
277 /** Check to see if the two symbol datas are in the same package.*/
278 private static boolean _areInSamePackage(SymbolData enclosingSD, SymbolData thisSD) {
279 String enclosingSDName = enclosingSD.getName();
280 int lastIndexOfDot = enclosingSDName.lastIndexOf('.');
281 if (lastIndexOfDot != -1) {
282
283 // Check for inner class names:
284 if (enclosingSD.getOuterData() != null) {
285 return _areInSamePackage(enclosingSD.getOuterData().getSymbolData(), thisSD);
286 }
287
288 enclosingSDName = enclosingSDName.substring(0, lastIndexOfDot);
289 }
290 else { enclosingSDName = ""; }
291 String thisSDName = thisSD.getName();
292 lastIndexOfDot = thisSDName.lastIndexOf('.');
293 if (lastIndexOfDot != -1) {
294
295 //check for inner class names:
296 if (thisSD.getOuterData() != null) {
297 return _areInSamePackage(enclosingSD, thisSD.getOuterData().getSymbolData());
298 }
299
300 thisSDName = thisSDName.substring(0, lastIndexOfDot);
301 }
302 else { thisSDName = ""; }
303 return enclosingSDName.equals(thisSDName);
304 }
305
306
307 /** Pass a default value*/
308 protected MethodData _lookupMethodHelper(String methodName, SymbolData enclosingSD, InstanceData[] arguments,
309 JExpression jexpr, boolean isConstructor, SymbolData thisSD) {
310 return _lookupMethodHelper(methodName, enclosingSD, arguments, jexpr, isConstructor, thisSD,
311 new LinkedList<MethodData>());
312 }
313
314 /** Finds and returns all matching methods. Assumes that the correct SymbolData is passed in.
315 * @param methodName The name of the method.
316 * @param enclosingSD The SymbolData we're currently searching for the method.
317 * @param arguments The types of the arguments to the method.
318 * @param jexpr The JExpression for the method invocation used in the error message.
319 * @param isConstructor True if this method is a constructor. If so, we know it must be in the initial SymbolData.
320 * @param thisSD The SymbolData whence the method is invoked.
321 * @return The pair of matching MethodData lists: without autoboxing and with autoboxing.
322 * An error is added if it is not found.
323 */
324 protected Pair<LinkedList<MethodData>, LinkedList<MethodData>>
325 _getMatchingMethods(String methodName, SymbolData enclosingSD, InstanceData[] arguments, JExpression jexpr,
326 boolean isConstructor, SymbolData thisSD) {
327 LinkedList<MethodData> mds = enclosingSD.getMethods();
328 Iterator<MethodData> iter = mds.iterator();
329 LinkedList<MethodData> matching = new LinkedList<MethodData>();
330 LinkedList<MethodData> matchingWithAutoBoxing = new LinkedList<MethodData>();
331 // if (enclosingSD.getName().equals("NonEmpty"))
332 // System.err.println("Starting search for method " + methodName + " in " + enclosingSD);
333 while (iter.hasNext()) {
334 MethodData md = iter.next();
335 // System.err.println("Testing method " + md.getName());
336 // if (md.getName().equals("NonEmpty")) {
337 // System.err.println("*** for NonEmpty(), params length = " + md.getParams().length + "; args length = "
338 // + arguments.length);
339 // }
340 // Check that the names match.
341 if (md.getName().equals(methodName) && md.getParams().length == arguments.length) {
342 VariableData[] vds = md.getParams();
343 int i;
344 boolean matches = true;
345
346 // First check to see if any methods exist that match the invocation without using autoboxing
347 for (i = 0; i < vds.length && i < arguments.length; i++) {
348 matches = matches &&
349 _isAssignableFromWithoutAutoboxing(vds[i].getType().getSymbolData(), arguments[i].getSymbolData());
350 if (matches == false) break;
351 }
352
353 // if all arguments checked out, check the access stuff.
354 if (matches && checkAccess(jexpr, md.getMav(), md.getName(), enclosingSD, thisSD, "method")) {
355 matching.addLast(md);
356 }
357
358 if (matches == false) { // Didn't match the method directly; try to match it with autoboxing
359 // if (enclosingSD.getName().equals("NonEmpty")) {
360 // System.err.println("*** Looking for autoboxing match for NonEmpty");
361 // System.err.println("vds = " + Arrays.toString(vds) + " arguments = " + Arrays.toString(arguments));
362 // }
363 matches = true;
364 // Now check to see if any methods exist that match the invocation while using autoboxing.
365 for (i = 0; i < vds.length && i < arguments.length; i++) {
366 if (enclosingSD.getName().equals("NonEmpty")) {
367 SymbolData parmSD = vds[i].getType().getSymbolData();
368 // System.err.println("vds[" + i + "].getType().getSymbolData() = " + parmSD);
369 SymbolData argSD = arguments[i].getSymbolData();
370 // System.err.println("arguments[" + i + "].getSymbolData() = " + argSD);
371 if (argSD.equals(SymbolData.INT_TYPE) && parmSD.equals(symbolTable.get("java.lang.Object")))
372 assert _isAssignableFrom(parmSD, argSD);
373 }
374 matches = matches && _isAssignableFrom(vds[i].getType().getSymbolData(), arguments[i].getSymbolData());
375 if (matches == false) {
376 if (enclosingSD.getName().equals("NonEmpty"))
377 // System.err.println("No match found for NonEmpty using autoboxing");
378 break;
379 }
380 }
381
382 // if all arguments checked out, check the access stuff.
383 if (matches && checkAccess(jexpr, md.getMav(), md.getName(), enclosingSD, thisSD, "method")) {
384 matchingWithAutoBoxing.addLast(md);
385 }
386 }
387 }
388 }
389
390 if (! isConstructor) {
391 for (SymbolData sup : enclosingSD.getInterfaces()) {
392 Pair<LinkedList<MethodData>, LinkedList<MethodData>> p =
393 _getMatchingMethods(methodName, sup, arguments, jexpr, isConstructor, thisSD);
394 matching.addAll(p.getFirst());
395 matchingWithAutoBoxing.addAll(p.getSecond());
396 }
397 if (enclosingSD.getSuperClass() != null) {
398 Pair<LinkedList<MethodData>, LinkedList<MethodData>> p =
399 _getMatchingMethods(methodName, enclosingSD.getSuperClass(), arguments, jexpr, isConstructor, thisSD);
400 matching.addAll(p.getFirst());
401 matchingWithAutoBoxing.addAll(p.getSecond());
402 }
403 }
404 // if (methodName.equals("NonEmpty")) {
405 // System.err.println("***** enclosingSD = " + enclosingSD + "; thisSD = " + thisSD + "; matching methods: "
406 // + matching);
407 // System.err.println("***** matching methods with autoboxing: " + matchingWithAutoBoxing);
408 // }
409
410 return new Pair<LinkedList<MethodData>, LinkedList<MethodData>> (matching, matchingWithAutoBoxing);
411 }
412
413 /** Finds which SymbolData this method is in, beginning at this SymbolData and recursively visiting super classes.
414 * @param methodName The name of the method.
415 * @param enclosingSD The SymbolData we're currently searching for the method.
416 * @param arguments The types of the arguments to the method.
417 * @param jexpr The JExpression for the method invocation used in the error message.
418 * @param isConstructor Tells us if this method is a constructor. If so, we know it must be in the initial SymbolData.
419 * We assume that the correct SymbolData was passed in.
420 * @param thisSD The SymbolData whence the method is invoked.
421 * @return The SymbolData where we find the method of null if it was not found. An error is added if it is not found.
422 */
423 protected MethodData _lookupMethodHelper(String methodName, SymbolData enclosingSD, InstanceData[] arguments,
424 JExpression jexpr, boolean isConstructor, SymbolData thisSD,
425 LinkedList<MethodData> matchingMethods) {
426 Pair<LinkedList<MethodData>, LinkedList<MethodData>> p = _getMatchingMethods(methodName, enclosingSD, arguments,
427 jexpr, isConstructor, thisSD);
428 LinkedList<MethodData> matching = p.getFirst();
429 LinkedList<MethodData> matchingWithAutoBoxing = p.getSecond();
430
431 // if this is not a constructor, matching and matchingWithAutoBoxing are both empty, and there is a outer data,
432 // then look at outer data
433 SymbolData currData = enclosingSD;
434 while (!isConstructor && matching.isEmpty() && matchingWithAutoBoxing.isEmpty() &&
435 currData.getOuterData() != null) {
436 currData = currData.getOuterData().getSymbolData();
437 p = _getMatchingMethods(methodName, currData, arguments, jexpr, isConstructor, thisSD);
438 matching = p.getFirst();
439 matchingWithAutoBoxing = p.getSecond();
440 }
441
442 if (matching.size() == 1) return matching.getFirst();
443 if (matching.size() > 1) return selectMostSpecificMethod(matching, arguments, jexpr);
444 if (matchingWithAutoBoxing.size() == 1) return matchingWithAutoBoxing.getFirst();
445 if (matchingWithAutoBoxing.size() > 1) return selectMostSpecificMethod(matchingWithAutoBoxing, arguments, jexpr);
446
447 //if nothing matched at all, return null
448 return null;
449 }
450
451 /** Finds which SymbolData this method is in, beginning at this SymbolData and recursively visiting super classes.
452 * Adds an error if the method is not found.
453 * @param methodName The name of the method.
454 * @param enclosingSD The SymbolData we're currently searching for the method.
455 * @param arguments The instance types of the arguments to the method.
456 * @param jexpr The JExpression for the method invocation used in the error message.
457 * @param errorMsg The error to be displayed if the method cannot be found.
458 * @param isConstructor True if this method is a constructor. If so, we know it must be in the initial SymbolData.
459 * We assume that the correct SymbolData was passed in.
460 * @param thisSD The SymbolData of the initial SymbolData.
461 * @return The SymbolData containging the method of null if it was not found. An error is added if it is not found.
462 */
463 protected MethodData _lookupMethod(String methodName, SymbolData enclosingSD, InstanceData[] arguments,
464 JExpression jexpr, String errorMsg, boolean isConstructor, SymbolData thisSD) {
465 // if (enclosingSD.isContinuation() && jexpr != null) {
466 // SymbolData newSD = getSymbolData(enclosingSD.getName(), enclosingSD, jexpr);
467 // if (newSD != null) {
468 // System.err.println("Replacing " + enclosingSD + " by " + newSD);
469 // if (methodName == "getName()" || methodName == "getName") {
470 // System.err.println("The methods of " + enclosingSD + " are: \n" + enclosingSD.getMethods());
471 // throw new UnexpectedException("Computation aborted");
472 //// System.err.println("The methods of " + newSD + " are: \n" + newSD.getMethods());
473 // }
474 // enclosingSD = newSD;
475 // }
476 // }
477 if (! isConstructor && methodName.equals(LanguageLevelVisitor.getUnqualifiedClassName(enclosingSD.getName()))) {
478 _addError("The keyword 'new' is required to invoke a constructor", jexpr);
479 }
480
481 MethodData md = _lookupMethodHelper(methodName, enclosingSD, arguments, jexpr, isConstructor, thisSD);
482 if (md != null) {
483 return md;
484 }
485 // None of the methods matched the signature.
486 StringBuffer message = new StringBuffer(errorMsg + methodName);
487 message.append("(");
488 if (arguments.length > 0) {
489 message.append(arguments[0].getName());
490 for (int i = 1; i < arguments.length; i++) {
491 message.append(", " + arguments[i].getName());
492 }
493 }
494 message.append(").");
495 _addError(message.toString(), jexpr);
496 // If not found, return null and have the caller check for it.
497 return null;
498 }
499
500 /** Assumes that all methods in list have the same name and the same number of arguments. Selects the method with
501 * the most specific signature. Assumes that each method does not require the application of any 1.5+ specific
502 * features.
503 * @param list The list of methods used
504 * @return The most specific method among the methods in the given list
505 */
506 private static MethodData selectMostSpecificMethod(List<MethodData> list, InstanceData[] arguments,
507 JExpression jexpr) {
508 if (list.isEmpty()) return null;
509 Iterator<MethodData> it = list.iterator();
510 MethodData best = it.next();
511 MethodData ambiguous = null; // there is no ambiguous other method at first
512 while (it.hasNext()) {
513 MethodData curr = it.next();
514 SymbolData[] bestParams = new SymbolData[best.getParams().length];
515 SymbolData[] currParams = new SymbolData[curr.getParams().length];
516
517 boolean better1 = false; // whether 'best' is better than 'curr'
518 boolean better2 = false; // whether 'curr' is better than 'best'
519 for (int i = 0; i < bestParams.length; i++) {
520 SymbolData bp = best.getParams()[i].getType().getSymbolData();
521 SymbolData cp = curr.getParams()[i].getType().getSymbolData();
522 boolean fromCurrToBest = cp.isAssignableTo(bp, LanguageLevelConverter.OPT.javaVersion());
523 boolean fromBestToCurr = bp.isAssignableTo(cp, LanguageLevelConverter.OPT.javaVersion());
524 bestParams[i] = bp;
525 currParams[i] = cp;
526
527 if (fromBestToCurr && ! fromCurrToBest) { // best's parameter[i] is more specific than curr's
528 better1 = true; // so best is better than curr
529 }
530 if (fromCurrToBest && ! fromBestToCurr) { // curr's parameter[i] is more specific than best's
531 better2 = true; // so curr is better than best
532 }
533 }
534
535 // decide which is more specific or whether they are ambiguous
536 if (better1 == better2) { // neither is better than the other
537 // Handle overridden methods
538 if (Arrays.equals(bestParams, currParams)) {
539 SymbolData c1 = best.getSymbolData();
540 SymbolData c2 = curr.getSymbolData();
541 boolean c1IsSuperOrSame = c2.isAssignableTo(c1, LanguageLevelConverter.OPT.javaVersion());
542 boolean c2IsSuperOrSame = c1.isAssignableTo(c2, LanguageLevelConverter.OPT.javaVersion());
543 if (c1IsSuperOrSame && !c2IsSuperOrSame) { // c2 is more specific
544 best = curr;
545 continue;
546 }
547 else if (c2IsSuperOrSame && !c1IsSuperOrSame) { // c1 is more specific
548 continue;
549 }
550 }
551 ambiguous = curr;
552 }
553 else if (better2) {
554 best = curr;
555 ambiguous = null; // no more ambiguity
556 }
557 }
558 if (ambiguous != null) {
559 StringBuffer invokeArgs = new StringBuffer("(");
560 StringBuffer ambigArgs = new StringBuffer("(");
561 StringBuffer bestArgs = new StringBuffer("(");
562 for (int i = 0; i<arguments.length; i++) {
563 if (i>0) {
564 invokeArgs.append(", ");
565 ambigArgs.append(", ");
566 bestArgs.append(", ");
567 }
568 invokeArgs.append(arguments[i].getSymbolData().getName());
569 ambigArgs.append(ambiguous.getParams()[i].getType().getSymbolData().getName());
570 bestArgs.append(best.getParams()[i].getType().getSymbolData().getName());
571 }
572 invokeArgs.append(")");
573 ambigArgs.append(")");
574 bestArgs.append(")");
575 _addError(best.getName() + invokeArgs.toString() + " is an ambiguous invocation. It matches both "
576 + best.getName() + bestArgs.toString() + " and " + ambiguous.getName() + ambigArgs.toString(),
577 jexpr);
578 }
579 return best;
580 }
581
582
583 /**
584 * Checks that the method is accessible given the SymbolData it's in and the current SymbolData.
585 * We have already checked the modifiers for validity.
586 * This will also work when checking the accessibility of static inner classes by passing in the
587 * static inner class for enclosingSD.
588 * @param mav The modifiers and visibility of the thing we're checking
589 * @param name The name of the thing we're checking
590 * @param enclosingSD The SymbolData which encloses name
591 * @param thisSD The SymbolData that is being type checked (from which we're referencing name)
592 * @param dataType The String that tells us whether name is a field, method, or class.
593 */
594 public static boolean checkAccess(JExpression piece, ModifiersAndVisibility mav, String name,
595 SymbolData enclosingSD, SymbolData thisSD, String dataType) {
596 return checkAccess(piece, mav, name, enclosingSD, thisSD, dataType, true);
597 }
598
599
600 /** Call this version when you don't want to add an error--only take in what is necessary to do the check, give default
601 * values for anything that will not be used.
602 */
603 public static boolean checkAccess(ModifiersAndVisibility mav, SymbolData enclosingSD, SymbolData thisSD) {
604 return checkAccess(NULL_LITERAL, mav, "", enclosingSD, thisSD, "", false);
605 }
606
607 /** Determines if thisSD can reference specified name defined in enclosingSD. Empty symbol is OK. See preceding
608 * method body.
609 * @param mav ??
610 */
611 public static boolean checkAccess(JExpression piece, ModifiersAndVisibility mav, String name,
612 SymbolData enclosingSD, SymbolData thisSD, String dataType, boolean addError) {
613 // if (piece instanceof VariableReference) System.err.println("**** Checking access of " + piece + "\nin " + enclosingSD);
614 if (thisSD.isOuterData(enclosingSD) || enclosingSD.isOuterData(thisSD) || thisSD.equals(enclosingSD)) {
615 return true;
616 }
617
618 // Check for the public modifier.
619 if (Utilities.isPublic(mav)) {
620 Data enclosingOuter = enclosingSD.getOuterData();
621 if (enclosingOuter == null) return true;
622 if (enclosingOuter instanceof SymbolData) {
623 return checkAccess(piece, mav, name, enclosingSD.getOuterData().getSymbolData(), thisSD, dataType, addError);
624 }
625 throw new RuntimeException("Internal Program Error: Trying to reference " + name +
626 "which is a member of something other than a class from outside of that thing. " +
627 "Please report this bug.");
628 }
629
630 // Check for the private modifier.
631 if (Utilities.isPrivate(mav)) {
632 /* As long as one of the symbol datas is the outer data of the other one (at some point down the data chain),
633 * private classes/methods/fields can be seen. Since we would have already returned, just throw the error and
634 * return false. */
635 // Utilities.show(thisSD + " cannot access symbol '" + name + "' within " + enclosingSD + " from '" + thisSD
636 // + "' in file " + piece.getSourceInfo().getFile());
637 // if (name.equals("evalVisitor")) throw new UnexpectedException("evalVisitor BUG");
638 String nameWithDots = Data.dollarSignsToDots(name);
639 String enclosingWithDots = Data.dollarSignsToDots(enclosingSD.getName());
640
641 // The following is a kludge to eliminate SOME spurious results.
642 if (nameWithDots.equals(enclosingWithDots) && enclosingSD.isPrimitiveType()) return true;
643
644 String siteName = Data.dollarSignsToDots(thisSD.getName());
645 if (addError) {
646 _addError("The " + dataType + " " + nameWithDots + " in " + enclosingWithDots +
647 " is private and cannot be accessed from " + siteName,
648 piece);
649 // throw new UnexpectedException("Generate Debugging Trace for access failure to " + nameWithDots + " in "
650 // + enclosingWithDots + " from " + siteName); // DEBUG
651 }
652 // Utilities.show("enclosingSD = " + enclosingSD + " thisSD = " + thisSD);
653 return false;
654 }
655
656 // Check for the protected modifier.
657 if (Utilities.isProtected(mav)) {
658 // Compare the package names. Remember that inner classes have '$' in their names. NO! TODO: Fix this !!!
659 if (_areInSamePackage(enclosingSD, thisSD)) {
660 return true;
661 }
662 // Check if thisSD is a subclass of enclosingSD.
663 // If they are in the same file, they are in the same package, so this does not need to check outer classes,
664 // only super classes and interfaces.
665 if (thisSD.isSubClassOf(enclosingSD)) {
666 return true;
667 }
668 else {
669 if (addError) _addError("The " + dataType + " " + Data.dollarSignsToDots(name) +
670 " is protected and cannot be accessed from " + Data.dollarSignsToDots(thisSD.getName()),
671 piece);
672 return false;
673 }
674 }
675
676 // Check the default "package modifier"
677 if (_areInSamePackage(enclosingSD, thisSD)) return true;
678 else {
679 if (addError) _addError("The " + dataType + " " + Data.dollarSignsToDots(name) +
680 " is package protected because there is no access specifier and cannot be accessed from "
681 + Data.dollarSignsToDots(thisSD.getName()),
682 piece);
683 return false;
684 }
685 }
686
687 /** Return the field or variable with the name text inside of data. (Referenced from thisSD) */
688 public static VariableData getFieldOrVariable(String text, Data data, SymbolData thisSD, JExpression piece) {
689 if (data == null) return null;
690 return getFieldOrVariable(text, data, thisSD, piece, data.getVars(), true, true);
691 }
692
693 public static VariableData getFieldOrVariable(String text, Data data, SymbolData thisSD, JExpression piece,
694 LinkedList<VariableData> vars) {
695 return getFieldOrVariable(text, data, thisSD, piece, vars, false, true);
696 }
697
698
699 public static VariableData getFieldOrVariable(String text, Data data, SymbolData thisSD, JExpression piece,
700 LinkedList<VariableData> vars, boolean shouldRecur) {
701 return getFieldOrVariable(text, data, thisSD, piece, vars, shouldRecur, true);
702 }
703
704 /** This method checks if the identifier called text is a field or variable visible from the context of the param
705 * "data" and is accessible from the context of thisSD. This will search through all of "data"'s enclosing classes
706 * (outer class, superclass, interfaces) recursively. This could mean a lot of recursion...
707 * Returns null if the field or variable is not found.
708 */
709 public static VariableData getFieldOrVariable(String text, Data data, SymbolData thisSD, JExpression piece,
710 LinkedList<VariableData> vars, boolean shouldRecur, boolean addError) {
711 VariableData vd = null;
712 if (data == null) return null;
713
714 Iterator<VariableData> iter = vars.iterator();
715 while (iter.hasNext()) {
716 vd = iter.next();
717 if (vd != null) {
718 if (vd.getName().equals(text)) {
719 if (vd.getEnclosingData() instanceof MethodData && addError) { // was BodyData
720 /* If vd is defined in a method and thisSD is a local class or anonymous inner class defined in data, then
721 * vd must be final to be used. Note: the enclosingData of formal parameters is the enclosing class not
722 * the corresponding method.
723 */
724 if (thisSD.isOuterData(vd.getEnclosingData())) {
725 if (! vd.isFinal() && addError) {
726 _addError("Local variable " + vd.getName()
727 + " is accessed from within an inner class; must be declared final", piece);
728 }
729 }
730 }
731 if (addError) {
732 checkAccess(piece, vd.getMav(), vd.getName(), vd.getEnclosingData().getSymbolData(), thisSD,
733 "field or variable");
734 }
735 return vd;
736 }
737 }
738 }
739
740 if (shouldRecur) {
741 /* The enclosingData should contain the super class, then the interfaces, and then the
742 * outer class. */
743
744 Iterator<Data> enclosingData = data.getEnclosingData().iterator();
745 while (enclosingData.hasNext()) {
746 Data outerData = enclosingData.next();
747 if (outerData == null) return null;
748 vd = getFieldOrVariable(text, outerData, thisSD, piece, outerData.getVars(), true, addError);
749 if (vd != null) return vd;
750 }
751 }
752 return null;
753 }
754
755 /** Return true iff type is an instance type. If not, add an error. */
756 public boolean assertInstanceType(TypeData type, String errorMsg, JExpression expression) {
757 if (! type.isInstanceType()) {
758 _addError(errorMsg + ". Perhaps you meant to create a new instance of " + type.getName(), expression);
759 return false;
760 }
761 return true;
762 }
763
764 /** If type is a PackageData, then it could not be resolved. Give an error*/
765 public boolean assertFound(TypeData type, JExpressionIF expression) {
766 if (type instanceof PackageData) {
767 _addError("Could not resolve symbol " + type.getName(), expression);
768 return false;
769 }
770 return true;
771 }
772
773 /** Adds an error pair consisting of the specified String message and JExpression. Sets _errorAdded to true.
774 * @param message Error message
775 * @param that The JExpression corresponding to where the error is.
776 */
777 protected static void _addError(String message, JExpressionIF that) {
778 _errorAdded = true;
779 Pair<String, JExpressionIF> p = new Pair<String, JExpressionIF>(message, that);
780 if (! errors.contains(p)) errors.addLast(p);
781 }
782
783 /** Return a TypeData array of the specified size. */
784 protected TypeData[] makeArrayOfRetType(int len) { return new TypeData[len]; }
785
786 /** This method is called by default from cases that do not
787 * override forCASEOnly.
788 **/
789 protected TypeData defaultCase(JExpressionIF that) { return null; }
790
791 public TypeData forClassDefOnly(ClassDef that, TypeData mavRes, TypeData nameRes, TypeData[] typeParametersRes,
792 TypeData superRes, TypeData[] interfacesRes, TypeData bodyRes) {
793 return forJExpressionOnly(that);//forTypeDefBaseOnly(that, mavRes, nameRes, typeParametersRes);
794 }
795
796 public TypeData forInnerClassDefOnly(InnerClassDef that, TypeData mavRes, TypeData nameRes, TypeData[] typeParamRes,
797 TypeData superClassRes, TypeData[] interfacesRes, TypeData bodyRes) {
798 return forJExpressionOnly(that);
799 // replaces forClassDefOnly(that, mavRes, nameRes, typeParamRes, superClassRes, interfacesRes,
800 // bodyRes);
801 }
802
803 public TypeData forInterfaceDefOnly(InterfaceDef that, TypeData mavRes, TypeData nameRes,
804 TypeData[] typeParamRes, TypeData[] superinterfacesRes, TypeData bodyRes) {
805 return forJExpressionOnly(that); //forTypeDefBaseOnly(that, mavRes, nameRes, typeParamRes);
806 }
807
808 public TypeData forInnerInterfaceDefOnly(InnerInterfaceDef that, TypeData mavRes, TypeData nameRes,
809 TypeData[] typeParamRes, TypeData[] superinterfacesRes, TypeData bodyRes) {
810 return forJExpressionOnly(that);
811 // replaces forInterfaceDefOnly(that, mavRes, nameRes, typeParamRes, superinterfacesRes, bodyRes);
812 }
813
814 public TypeData forInstanceInitializerOnly(InstanceInitializer that, TypeData codeRes) {
815 return forJExpressionOnly(that);//forInitializerOnly(that, codeRes);
816 }
817
818 public TypeData forStaticInitializerOnly(StaticInitializer that, TypeData codeRes) {
819 return forJExpressionOnly(that);//InitializerOnly(that, codeRes);
820 }
821
822 public TypeData forLabeledStatementOnly(LabeledStatement that, TypeData statementRes) {
823 return forJExpressionOnly(that);//StatementOnly(that);
824 }
825
826 public TypeData forBlockOnly(Block that, TypeData[] statementsRes) {
827 return forJExpressionOnly(that);//StatementOnly(that);
828 }
829
830 public TypeData forExpressionStatementOnly(ExpressionStatement that, TypeData exprRes) {
831 return forJExpressionOnly(that);//StatementOnly(that);
832 }
833
834 public TypeData forSwitchStatementOnly(SwitchStatement that, TypeData testRes, TypeData[] casesRes) {
835 return forJExpressionOnly(that);//tatementOnly(that);
836 }
837
838 public TypeData forBreakStatementOnly(BreakStatement that) {
839 // returns a sentinel value that tells us that this is a break or continue statement.
840 return SymbolData.NOT_FOUND;
841 }
842
843 public TypeData forContinueStatementOnly(ContinueStatement that) {
844 // returns a sentinel value that tells us that this is a break or continue statement.
845 return SymbolData.NOT_FOUND;
846 }
847
848 public TypeData forReturnStatementOnly(ReturnStatement that) {
849 return forJExpressionOnly(that);//StatementOnly(that);
850 }
851
852 public TypeData forTryCatchStatementOnly(TryCatchStatement that, TypeData tryBlockRes,
853 TypeData[] catchBlocksRes) {
854 return forJExpressionOnly(that);//StatementOnly(that);
855 }
856
857 public TypeData forMethodDefOnly(MethodDef that, TypeData mavRes, TypeData[] typeParamsRes,
858 TypeData resRes, TypeData nameRes, TypeData paramsRes,
859 TypeData[] throwsRes) {
860 return resRes; //forJExpressionOnly(that);
861 }
862 public TypeData forConcreteMethodDefOnly(ConcreteMethodDef that, TypeData mavRes, TypeData[] typeParamsRes,
863 TypeData resRes, TypeData nameRes, TypeData paramsRes,
864 TypeData[] throwsRes, TypeData bodyRes) {
865 return forMethodDefOnly(that, mavRes, typeParamsRes, resRes, nameRes, paramsRes,
866 throwsRes);
867 }
868
869 /** Returns the common super type of the two types provided.*/
870 protected SymbolData getCommonSuperTypeBaseCase(SymbolData sdLeft, SymbolData sdRight) {
871 if (sdLeft == SymbolData.EXCEPTION) { return sdRight; }
872 if (sdRight == SymbolData.EXCEPTION) { return sdLeft; }
873 if (_isAssignableFrom(sdLeft, sdRight)) {return sdLeft;}
874 if (_isAssignableFrom(sdRight, sdLeft)) {return sdRight;}
875 return null;
876 }
877
878 /** Return whether the value on the right can be assigned to the value on the left.
879 * Uses autoboxing if the user has java 1.5.
880 */
881 protected static boolean _isAssignableFrom(SymbolData sdLeft, SymbolData sdRight) {
882 if (sdRight == null) return false;
883 // System.err.println("Java Version = " + LanguageLevelConverter.OPT.javaVersion());
884 assert LanguageLevelConverter.versionSupportsAutoboxing(LanguageLevelConverter.OPT.javaVersion());
885 return sdRight.isAssignableTo(sdLeft, LanguageLevelConverter.OPT.javaVersion());
886 }
887
888 /** Return whether the value on the right can be assigned to the value on the left. Does not use autoboxing. */
889 protected boolean _isAssignableFromWithoutAutoboxing(SymbolData sdLeft, SymbolData sdRight) {
890
891 if (sdRight == null) { return false; }
892 return sdRight.isAssignableTo(sdLeft, JavaVersion.JAVA_1_4); // Version 1.4 used to turn off autoboxing
893 }
894
895 /**
896 * The method will add an error for each abstract method in the current SymbolData's inheritance hierarchy
897 * that does not have a concrete implementation.
898 * @param sd The SymbolData for the current class definition
899 * @param classDef The current ClassDef
900 */
901 protected void _checkAbstractMethods(SymbolData sd, JExpression classDef) {
902 if (sd.hasModifier("abstract") || sd.isInterface()) {
903 // Our work here is done because an abstract class has no constraints to implement.
904 return;
905 }
906 LinkedList<MethodData> mds = sd.getMethods();
907
908 //add all concrete methods from our super classes--this is to fix the case where you and your super class extend
909 //the same interface, and your super class concretely implements the methods--you do not have to also implement them.
910 //there are other generalizations of this case.
911 //TODO: Consider optimizing this--it may be slow.
912 LinkedList<MethodData> cmds = _cloneMethodDataList(mds);
913 SymbolData superD = sd.getSuperClass();
914 while (superD != null && !superD.getName().equals("java.lang.Object")) {
915 LinkedList<MethodData> smds = superD.getMethods();
916 for (MethodData md: smds) {
917 if (!md.hasModifier("abstract")) {// && SymbolData.repeatedSignature(cmds, md, false) == null) {
918 cmds.addLast(md);
919 }
920 }
921 superD = superD.getSuperClass();
922 }
923
924 // TODO: Check all enclosingDatas (interfaces too).
925 _checkAbstractMethodsHelper(sd, sd.getSuperClass(), cmds, classDef);
926 //check interfaces as well:
927 for (SymbolData iSD: sd.getInterfaces()) {
928 _checkAbstractMethodsHelper(sd, iSD, cmds, classDef);
929 }
930 }
931
932 /**
933 * This checks a superclass of the original SymbolData. If this class is not abstract, then we are
934 * done because this concrete class must have implemented all the abstract methods above it.
935 * @param origSd The SymbolData for the current class definition
936 * @param sd The SymbolData for the class in origSd's superclass hierarchy that we're currently examining
937 * @param concreteMds The list of MethodDatas of concrete methods that we've seen so far
938 * @param classDef The original ClassDef
939 */
940 private void _checkAbstractMethodsHelper(SymbolData origSd, SymbolData sd, LinkedList<MethodData> concreteMds,
941 JExpression classDef) {
942 if (sd == null || (!sd.hasModifier("abstract") && !sd.isInterface())) {
943 return;
944 }
945 // Gets its abstract methods and make sure they're in concreteMds.
946 LinkedList<MethodData> mds = sd.getMethods();
947 Iterator<MethodData> iter = mds.iterator();
948 while (iter.hasNext()) {
949 MethodData md = iter.next();
950 if (md.hasModifier("abstract")) {
951 // Check if this md is overridden by one of the MethodDatas in the list of concreteMds.
952 MethodData matchingMd = SymbolData.repeatedSignature(concreteMds, md);
953 if (matchingMd == null) {
954 StringBuffer message;
955 if (classDef instanceof AnonymousClassInstantiation) {
956 message = new StringBuffer("This anonymous inner class must override the abstract method: " + md.getName());
957 }
958 else {
959 message = new StringBuffer(origSd.getName()
960 + " must be declared abstract or must override the abstract method: "
961 + md.getName());
962 }
963 VariableData[] params = md.getParams();
964 InstanceData[] arguments = new InstanceData[params.length];
965 for (int i = 0; i < params.length; i++) {
966 arguments[i] = params[i].getType().getInstanceData();
967 }
968 message.append("(");
969 if (arguments.length > 0) {
970 message.append(arguments[0].getName());
971 for (int i = 1; i < arguments.length; i++) {
972 message.append(", " + arguments[i].getName());
973 }
974 }
975 message.append(") in " + sd.getName());
976 _addError(message.toString(), classDef);
977 }
978 }
979 else {
980 // Update the list of concrete methods.
981 concreteMds.addLast(md);
982 }
983 }
984 //check the super class
985 _checkAbstractMethodsHelper(origSd, sd.getSuperClass(), _cloneMethodDataList(concreteMds), classDef);
986
987 //check the interfaces
988 for (SymbolData iSD: sd.getInterfaces()) {
989 _checkAbstractMethodsHelper(origSd, iSD, _cloneMethodDataList(concreteMds), classDef);
990 }
991 }
992
993 /**Create a new LinkedList that holds the method datas in mds*/
994 private LinkedList<MethodData> _cloneMethodDataList(LinkedList<MethodData> mds) {
995 LinkedList<MethodData> toReturn = new LinkedList<MethodData>();
996 for (int i = 0; i<mds.size(); i++) {
997 toReturn.addLast(mds.get(i));
998 }
999 return toReturn;
1000 }
1001
1002 /**
1003 * Make a copy of the provided symbol data list, and return that copy.
1004 * @param l The LinkedList of SymbolDatas to copy.
1005 * @return A copy of l.
1006 */
1007 private LinkedList<SymbolData> cloneSDList(LinkedList<SymbolData> l) {
1008 LinkedList<SymbolData> l2 = new LinkedList<SymbolData>();
1009 for (int i = 0; i< l.size(); i++) {
1010 l2.addLast(l.get(i));
1011 }
1012 return l2;
1013 }
1014
1015 /**
1016 * Checks for cyclic inheritance by traversing sd's list of superclasses and interfaces and checking if we've
1017 * seen them before. We accumulate the list of classes and interfaces we've seen before in hierarchy.
1018 * @param sd The SymbolData we're currently checking
1019 * @param hierarchy The list of classes and interfaces we've seen before.
1020 * @return Whether there is cyclic inheritance
1021 */
1022 protected boolean checkForCyclicInheritance(SymbolData sd, LinkedList<SymbolData> hierarchy, TypeDefBase tdb) {
1023 if (sd==null || LanguageLevelVisitor.isJavaLibraryClass(sd.getName())) {
1024 // We can stop checking because these can't have cyclic inheritance
1025 return false;
1026 }
1027
1028
1029 if (hierarchy.contains(sd)) {
1030 _addError("Cyclic inheritance involving " + sd.getName(), tdb);
1031 return true;
1032 }
1033
1034 //add current symbol data to the hierarchy
1035 hierarchy.addLast(sd);
1036
1037 //Also add inner classes
1038 LinkedList<SymbolData> innerClasses = sd.getInnerClasses();
1039 for (int i = 0; i < innerClasses.size(); i++) {
1040 hierarchy.addLast(innerClasses.get(i));
1041 }
1042
1043 LinkedList<SymbolData> clonedHierarchy = cloneSDList(hierarchy);
1044 boolean doReturn = checkForCyclicInheritance(sd.getSuperClass(), clonedHierarchy, tdb);
1045
1046 //check the interfaces
1047 for (SymbolData iSD: sd.getInterfaces()) {
1048 doReturn |= checkForCyclicInheritance(iSD, clonedHierarchy, tdb);
1049 }
1050
1051 return doReturn;
1052 }
1053
1054 /**Do what is necessary to handle a class def */
1055 public TypeData forClassDef(ClassDef that) {
1056
1057 SymbolData objectSD = getSymbolData("java.lang.Object", that, true, false);
1058 assert SymbolData.INT_TYPE.isAssignableTo(objectSD, LanguageLevelConverter.OPT.javaVersion());
1059
1060 String className = getQualifiedClassName(that.getName().getText());
1061 SymbolData sd = getSymbolData(className, that, true, false);
1062 if (sd == null) {
1063 // System.err.println("****DISASTER**** sd is null for ClassDef " + className);
1064 _addError("The class " + className + " was never defined", that);
1065 _log.log("*********DISASTER**** sd is null for ClassDef " + className);
1066 return null;
1067 }
1068 // Check for cyclic inheritance
1069 if (checkForCyclicInheritance(sd, new LinkedList<SymbolData>(), that)) {
1070 return null;
1071 }
1072
1073 // See if sd is public. If so, the filename must match sd's name.
1074 if (sd.hasModifier("public")) {
1075 String fileName = className.replace('.', System.getProperty("file.separator").charAt(0));
1076 if (!_file.getAbsolutePath().endsWith(fileName + ".dj") &&
1077 !_file.getAbsolutePath().endsWith(fileName + ".dj0") &&
1078 !_file.getAbsolutePath().endsWith(fileName + ".dj1") &&
1079 !_file.getAbsolutePath().endsWith(fileName + ".dj2")) {
1080 _addError(className + " is public thus must be defined in a file with the same name.", that.getName());
1081 }
1082 }
1083
1084 // Reset sd's anonymous inner class count so we can count again during this second pass.
1085 sd.setAnonymousInnerClassNum(0);
1086
1087 // Make sure this class does not implement the Runnable interface
1088 if (sd.hasInterface(getSymbolData("java.lang.Runnable", that, false, false))) {
1089 _addError(sd.getName() + " implements the Runnable interface, which is not allowed at any language level", that);
1090 }
1091
1092 SymbolData superClass = sd.getSuperClass();
1093 // make sure this class can see its super class
1094 if (superClass != null) {
1095 checkAccess(that.getSuperclass(), superClass.getMav(), superClass.getName(), superClass, sd, "class");
1096 //Also make sure that the superClass is not an interface!
1097 if (superClass.isInterface()) {
1098 _addError(superClass.getName() + " is an interface and thus cannot appear after the keyword 'extends' here. "
1099 + "Perhaps you meant to say 'implements'?", that);
1100 }
1101 }
1102
1103 //make sure that the class def does not extend a final class.
1104 if (superClass != null && superClass.hasModifier("final")) {
1105 _addError("Class " + sd.getName() + " cannot extend the final class " + superClass.getName(), that);
1106 return sd;
1107 }
1108
1109 final TypeData mavRes = that.getMav().visit(this);
1110 final TypeData nameRes = that.getName().visit(this);
1111 final TypeData[] typeParamRes = makeArrayOfRetType(that.getTypeParameters().length);
1112 for (int i = 0; i < that.getTypeParameters().length; i++) {
1113 typeParamRes[i] = that.getTypeParameters()[i].visit(this);
1114 }
1115 final TypeData superClassRes = that.getSuperclass().visit(this);
1116 final SymbolData[] interfacesRes = new SymbolData[that.getInterfaces().length];
1117 for (int i = 0; i < that.getInterfaces().length; i++) {
1118 interfacesRes[i] = getSymbolData(that.getInterfaces()[i].getName(), that.getInterfaces()[i], true, true);
1119 if (interfacesRes[i] != null) {
1120 //make sure this class can see the interfaces it implements
1121 checkAccess(that.getInterfaces()[i], interfacesRes[i].getMav(), interfacesRes[i].getName(), interfacesRes[i], sd,
1122 "interface");
1123 //Make sure that all of these are actually interfaces.
1124 if (!interfacesRes[i].isInterface()) {
1125 _addError(interfacesRes[i].getName() +
1126 " is not an interface and thus cannot appear after the keyword 'implements' here. " +
1127 "Perhaps you meant to say 'extends'?", that);
1128 }
1129
1130 }
1131 else {
1132 throw new RuntimeException("Internal Program Error: getSymbolData( " + that.getInterfaces()[i].getName() +
1133 ") returned null. Please report this bug.");
1134 }
1135 }
1136
1137 //See if sd is a test class. If so, it must be public.
1138 SymbolData testSd = getSymbolData("junit.framework.TestCase", NULL_LITERAL, false, true);
1139 if (testSd != null && sd.isSubClassOf(testSd)) {
1140 if (! sd.hasModifier("public")) {
1141 _addError(sd.getName() + " extends TestCase and thus must be explicitly declared public", that);
1142 }
1143 boolean foundOne = false;
1144 for (int i = 0; i < sd.getMethods().size(); i++) {
1145 MethodData myMd = sd.getMethods().get(i);
1146 if (myMd.getName().startsWith("test") && (myMd.getReturnType() == SymbolData.VOID_TYPE) &&
1147 myMd.hasModifier("public")) {
1148 foundOne = true;
1149 break;
1150 }
1151 }
1152 if (! foundOne) {
1153 _addError("Class " + sd.getName() + " does not have any valid test methods. " +
1154 "Test methods must be declared public, must return void, and must start with the word \"test\"",
1155 that);
1156 }
1157 }
1158
1159 ClassBodyTypeChecker cbtc =
1160 new ClassBodyTypeChecker(sd, _file, _package, _importedFiles, _importedPackages, new LinkedList<VariableData>(),
1161 new LinkedList<Pair<SymbolData, JExpression>>());
1162
1163
1164 final TypeData bodyRes = that.getBody().visit(cbtc);
1165
1166 // Make sure that sd's methods override all of its hierarchy's abstract methods.
1167 _checkAbstractMethods(sd, that);
1168
1169
1170 //only do this if there were no constructors: make sure that all final fields were given a value.
1171 if (! cbtc.hasConstructor) {
1172 LinkedList<VariableData> sdVars = sd.getVars();
1173 for (int i = 0; i<sdVars.size(); i++) {
1174 if (!sdVars.get(i).hasValue()) {
1175 _addError("The final field " + sdVars.get(i).getName() + " has not been initialized", that);
1176 return null;
1177 }
1178 }
1179 }
1180
1181 // Unassign anything that got assigned in the scope of the class def.
1182 unassignVariableDatas(cbtc.thingsThatHaveBeenAssigned);
1183
1184 return forClassDefOnly(that, mavRes, nameRes, typeParamRes, superClassRes, interfacesRes, bodyRes);
1185 }
1186
1187 /**Do everything necessary to handle an interface*/
1188 public TypeData forInterfaceDef(InterfaceDef that) {
1189 String interfaceName = getQualifiedClassName(that.getName().getText());
1190 SymbolData sd = getSymbolData(interfaceName, that, true, false);
1191
1192 // //See if sd is public. If so, the filename must match sd's name.
1193 if (sd.hasModifier("public")) {
1194 String fileName = interfaceName.replace('.', System.getProperty("file.separator").charAt(0));
1195 if (!_file.getAbsolutePath().endsWith(fileName + ".dj") &&
1196 !_file.getAbsolutePath().endsWith(fileName + ".dj0") &&
1197 !_file.getAbsolutePath().endsWith(fileName + ".dj1") &&
1198 !_file.getAbsolutePath().endsWith(fileName + ".dj2")) {
1199 _addError(interfaceName + " is public thus must be defined in a file with the same name.", that.getName());
1200 }
1201 }
1202
1203 // Check for cyclic inheritance
1204 if (checkForCyclicInheritance(sd, new LinkedList<SymbolData>(), that)) {
1205 return null;
1206 }
1207
1208 // // Make sure that this does not extend the runnable interface
1209 // if (sd.hasInterface(getSymbolData("java.lang.Runnable", that, false, false))) {
1210 // _addError(sd.getName() + " extends the Runnable interface, which is not allowed at any language level", that);
1211 // }
1212
1213 final TypeData mavRes = that.getMav().visit(this);
1214 final TypeData nameRes = that.getName().visit(this);
1215 final TypeData[] typeParamRes = makeArrayOfRetType(that.getTypeParameters().length);
1216 for (int i = 0; i < that.getTypeParameters().length; i++) {
1217 typeParamRes[i] = that.getTypeParameters()[i].visit(this);
1218 }
1219 final SymbolData[] interfacesRes = new SymbolData[that.getInterfaces().length];
1220 for (int i = 0; i < that.getInterfaces().length; i++) {
1221 //superinterfacesRes[i] = that.getInterfaces()[i].visit(this);
1222 interfacesRes[i]=getSymbolData(that.getInterfaces()[i].getName(), that.getInterfaces()[i], true, true);
1223 if (interfacesRes[i] != null) {
1224 //make sure this class can see the interfaces it implements
1225 checkAccess(that.getInterfaces()[i], interfacesRes[i].getMav(), interfacesRes[i].getName(), interfacesRes[i], sd,
1226 "interface");
1227 if (!interfacesRes[i].isInterface()) {
1228 _addError(interfacesRes[i].getName() +
1229 " is not an interface and thus cannot appear after the keyword 'extends' here",
1230 that);
1231 }
1232 }
1233 else {
1234 throw new RuntimeException("Internal Program Error: getSymbolData( " + that.getInterfaces()[i].getName() +
1235 ") returned null. Please report this bug.");
1236 }
1237
1238 }
1239 InterfaceBodyTypeChecker ibtc =
1240 new InterfaceBodyTypeChecker(sd, _file, _package, _importedFiles, _importedPackages,
1241 new LinkedList<VariableData>(), new LinkedList<Pair<SymbolData, JExpression>>());
1242 final TypeData bodyRes = that.getBody().visit(ibtc);
1243 return forInterfaceDefOnly(that, mavRes, nameRes, typeParamRes, interfacesRes, bodyRes);
1244 }
1245
1246 /** Retrieve the class being imported from the Symbol table, and make sure it is public. */
1247 public TypeData forClassImportStatement(ClassImportStatement that) {
1248 CompoundWord cWord = that.getCWord();
1249 Word[] words = cWord.getWords();
1250
1251 StringBuffer name = new StringBuffer(words[0].getText());
1252 for (int i = 1; i < words.length; i++) {name.append('.' + words[i].getText());}
1253 //This makes sure the class can be imported and is in the right package.
1254 final TypeData cWordRes = getSymbolData(name.toString(), that, true, true);
1255 if (cWordRes != null) {
1256 if (! cWordRes.hasModifier("public")) {
1257 _addError(cWordRes.getName() + " is not public, and thus cannot be seen here", that);
1258 }
1259 }
1260 return forClassImportStatementOnly(that, cWordRes);
1261 }
1262
1263 /**
1264 * Make sure that the package being imported does not conflict with another class in the symbol table, since
1265 * we will not allow a package to be imported if it has the same name as a class.
1266 */
1267 public TypeData forPackageStatement(PackageStatement that) {
1268 //final SymbolData cWordRes = that.getCWord().visit(this);
1269 CompoundWord cWord = that.getCWord();
1270 Word[] words = cWord.getWords();
1271
1272 StringBuffer nameBuff = new StringBuffer(words[0].getText());
1273 for (int i = 1; i < words.length; i++) nameBuff.append('.' + words[i].getText());
1274 String name = nameBuff.toString();
1275
1276 /* It is sufficient to just use "get" directly from the symbolTable, becuase the first pass has resolved all
1277 * package names as symbols (??), so if we were successful, they will already be in symbol table. It is not
1278 * necessary to check accessibility, because somewhere along the line, the package must have conflicted with
1279 * a visible class--all top level classes must be public or package protected. */
1280 if (symbolTable.get(name) != null) {
1281 _addError(name + " is not a allowable package name, because it conflicts with a class you have already defined",
1282 that);
1283 }
1284 return null;
1285 }
1286
1287 /** @return the appropriate type for a primitive type.*/
1288 public TypeData forPrimitiveType(PrimitiveType that) {
1289 String text = that.getName();
1290 if (text.equals("boolean")) {
1291 return SymbolData.BOOLEAN_TYPE;
1292 }
1293 else if (text.equals("char")) {
1294 return SymbolData.CHAR_TYPE;
1295 }
1296 else if (text.equals("byte")) {
1297 return SymbolData.BYTE_TYPE;
1298 }
1299 else if (text.equals("short")) {
1300 return SymbolData.SHORT_TYPE;
1301 }
1302 else if (text.equals("int")) {
1303 return SymbolData.INT_TYPE;
1304 }
1305 else if (text.equals("long")) {
1306 return SymbolData.LONG_TYPE;
1307 }
1308 else if (text.equals("float")) {
1309 return SymbolData.FLOAT_TYPE;
1310 }
1311 else if (text.equals("double")) {
1312 return SymbolData.DOUBLE_TYPE;
1313 }
1314 else {
1315 throw new RuntimeException("Internal Program Error: Not a legal primitive type: " + text
1316 + ". Please report this bug");
1317 }
1318 }
1319
1320 /**Visit the type of the cast expression as well as the value being cast*/
1321 public TypeData forCastExpression(CastExpression that) {
1322 final TypeData typeRes = that.getType().visit(this);
1323 final TypeData valueRes = that.getValue().visit(this);
1324 if (valueRes == null) {
1325 // An error occurred type-checking the value; return the expected type to
1326 // allow type-checking to continue.
1327 return typeRes;
1328 }
1329 return forCastExpressionOnly(that, typeRes, valueRes);
1330 }
1331
1332 /**
1333 * Walk the provided list of VariableDatas, and set each one back to unassigned.
1334 * This is used to correct the changes to VariableDatas made when we change scope.
1335 * VariableDatas will only be in this list if they are assigned for the first time
1336 * in a context that we are now leaving.
1337 * @param toUnassign A LinkedList of VariableDatas to set back to unassigned.
1338 */
1339 void unassignVariableDatas(LinkedList<VariableData> toUnassign) {
1340 for (int i = 0; i<toUnassign.size(); i++) {
1341 toUnassign.get(i).lostValue();
1342 }
1343 }
1344
1345 /** Test the methods defined in the above class. */
1346 public static class TypeCheckerTest extends TestCase {
1347
1348 private TypeChecker _btc;
1349
1350 private SymbolData _sd1;
1351 private SymbolData _sd2;
1352 private SymbolData _sd3;
1353 private SymbolData _sd4;
1354 private SymbolData _sd5;
1355 private SymbolData _sd6;
1356 private SymbolData _object;
1357
1358 public TypeCheckerTest() { this(""); }
1359 public TypeCheckerTest(String name) { super(name); }
1360
1361 public void setUp() {
1362 errors = new LinkedList<Pair<String, JExpressionIF>>();
1363 LanguageLevelConverter.symbolTable.clear();
1364 LanguageLevelConverter.loadSymbolTable();
1365
1366 _btc = new TypeChecker(new File(""), "", new LinkedList<String>(), new LinkedList<String>());
1367 LanguageLevelConverter.OPT = new Options(JavaVersion.JAVA_5, EmptyIterable.<File>make());
1368 _btc._importedPackages.addFirst("java.lang");
1369 _errorAdded = false;
1370 _sd1 = new SymbolData("i.like.monkey");
1371 _sd2 = new SymbolData("i.like.giraffe");
1372 _sd3 = new SymbolData("zebra");
1373 _sd4 = new SymbolData("u.like.emu");
1374 _sd5 = new SymbolData("");
1375 _sd6 = new SymbolData("cebu");
1376
1377 _object = symbolTable.get("java.lang.Object");
1378 assert _object != null;
1379 // o.setIsContinuation(false);
1380 // o.setMav(_publicMav);
1381 // symbolTable.put("java.lang.Object", o);
1382
1383 assert symbolTable.get("java.lang.Double") != null;
1384 // Double.setIsContinuation(false);
1385 // Double.setMav(_publicMav);
1386 // Double.setSuperClass(o); // a white lie for this test
1387 // symbolTable.put("java.lang.Double", Double);
1388
1389 assert symbolTable.get("java.lang.Float") != null;
1390 // Float.setIsContinuation(false);
1391 // Float.setMav(_publicMav);
1392 // Float.setSuperClass(o); // a white lie for this test
1393 // symbolTable.put("java.lang.Float", Float);
1394
1395 assert symbolTable.get("java.lang.Long") != null;
1396 // Long.setIsContinuation(false);
1397 // Long.setMav(_publicMav);
1398 // Long.setSuperClass(o); // a white lie for this test
1399 // symbolTable.put("java.lang.Long", Long);
1400
1401 assert symbolTable.get("java.lang.Integer") != null;
1402 // Integer.setIsContinuation(false);
1403 // Integer.setMav(_publicMav);
1404 // Integer.setSuperClass(o); // a white lie for this test
1405 // symbolTable.put("java.lang.Integer", Integer);
1406
1407 assert symbolTable.get("java.lang.Short") != null;
1408 // Short.setIsContinuation(false);
1409 // Short.setMav(_publicMav);
1410 // Short.setSuperClass(o); // a white lie for this test
1411 // symbolTable.put("java.lang.Short", Short);
1412
1413 assert symbolTable.get("java.lang.Character") != null;
1414 // Character.setIsContinuation(false);
1415 // Character.setMav(_publicMav);
1416 // Character.setSuperClass(o); // a white lie for this test
1417 // symbolTable.put("java.lang.Character", Character);
1418
1419 assert symbolTable.get("java.lang.Byte") != null;
1420 // Byte.setIsContinuation(false);
1421 // Byte.setMav(_publicMav);
1422 // Byte.setSuperClass(o); // a white lie for this test
1423 // symbolTable.put("java.lang.Byte", Byte);
1424
1425 assert symbolTable.get("java.lang.Boolean") != null;
1426 // Boolean.setIsContinuation(false);
1427 // Boolean.setMav(_publicMav);
1428 // Boolean.setSuperClass(o); // a white lie for this test
1429 // symbolTable.put("java.lang.Boolean", Boolean);
1430
1431 assert symbolTable.get("java.lang.String") != null;
1432 // SymbolData string = new SymbolData("java.lang.String");
1433 // string.setIsContinuation(false);
1434 // string.setMav(_publicMav);
1435 // string.setSuperClass(_object); // a white lie for this test
1436 // symbolTable.put("java.lang.String", string);
1437 }
1438
1439 public void test_getData() {
1440 try {
1441 _btc._getData();
1442 fail("Should have thrown a RuntimeException");
1443 }
1444 catch (RuntimeException re) {
1445 }
1446 }
1447
1448 public void testGetSymbolData() {
1449 symbolTable.put("zebra", _sd3);
1450 _sd3.setIsContinuation(false);
1451 SymbolData sd = symbolTable.get("java.lang.Object");
1452 sd.setPackage("java.lang");
1453 assertEquals("Should get _sd3 from the Symboltable.", _sd3,
1454 _btc.getSymbolData("zebra", NULL_LITERAL, true, true));
1455 assertEquals("Should get sd from the Symboltable.", sd,
1456 _btc.getSymbolData("java.lang.Object", NULL_LITERAL, true, true));
1457 _btc.getSymbolData("koala", NULL_LITERAL, true, true);
1458
1459 assertEquals("Should be one error", 1, errors.size());
1460 assertEquals("ERror message should be correct ", "Class or variable koala not found.", errors.get(0).getFirst());
1461
1462 errors = new LinkedList<Pair<String, JExpressionIF>>();
1463
1464 sd.setPackage("notRightPackage");
1465 _btc.getSymbolData("java.lang.Object", NULL_LITERAL, true, true);
1466 assertEquals("Should be 1 error", 1, errors.size());
1467 assertEquals("Error message should be correct",
1468 "The class java.lang.Object is not in the right package. Perhaps you meant to package it?",
1469 errors.get(0).getFirst());
1470
1471 //looking up a class that implements runnable will give an error:
1472 SymbolData sdThread = new SymbolData("java.lang.Thread");
1473 sdThread.setIsContinuation(false);
1474 sdThread.setPackage("java.lang");
1475 symbolTable.put("java.lang.Thread", sdThread);
1476 assertEquals("Should return null", null, _btc.getSymbolData("Thread", NULL_LITERAL, true,
1477 true));
1478
1479 assertEquals("Should now be 2 errors", 2, errors.size());
1480 assertEquals("Error message should be correct", "java.lang.Thread implements the Runnable interface, " +
1481 "which is not allowed at any language level", errors.get(1).getFirst());
1482
1483 // a class that implements Runnable, but was not user defined and is not one of our specific classes known to
1484 // extend Runnable will not give an error
1485 SymbolData sdOther = new SymbolData("myClass");
1486 sdOther.setIsContinuation(false);
1487 SymbolData run = new SymbolData("java.lang.Runnable");
1488 run.setIsContinuation(false);
1489 run.setInterface(true);
1490 run.setPackage("java.lang");
1491 sdOther.addInterface(run);
1492
1493 symbolTable.put("myClass", sdOther);
1494 symbolTable.put("java.lang.Runnable", run);
1495
1496 assertEquals("Should return sdOther", sdOther, _btc.getSymbolData("myClass", NULL_LITERAL,
1497 true, true));
1498 assertEquals("Should still just be 2 errors", 2, errors.size());
1499
1500 //Test an inner class from the context of the outer class
1501 SymbolData sdInner = new SymbolData("outer.inner");
1502 sdInner.setIsContinuation(false);
1503 SymbolData sdOuter = new SymbolData("outer");
1504 sd.setIsContinuation(false);
1505 sdOuter.addInnerClass(sdInner);
1506 sdInner.setOuterData(sdOuter);
1507 assertEquals("Should return sdInner", sdInner, _btc.getSymbolData("inner", sdOuter,
1508 NULL_LITERAL));
1509
1510 //Test an inner interface from the context of the outer class
1511 sdInner.setInterface(true);
1512 sdOuter = new SymbolData("outer");
1513 sd.setIsContinuation(false);
1514 sdOuter.addInnerInterface(sdInner);
1515 assertEquals("Should return sdInner", sdInner, _btc.getSymbolData("inner", sdOuter,
1516 NULL_LITERAL));
1517
1518
1519 //Test that the correct inner class is returned
1520 //
1521 // C {
1522 // A {
1523 //
1524 // D{}
1525 // }
1526 // B{
1527 // D{}
1528 // }
1529 // }
1530
1531 SymbolData sd1 = new SymbolData("C.A");
1532 SymbolData sd2 = new SymbolData("C.B");
1533 SymbolData sd3 = new SymbolData("C");
1534 SymbolData sd4 = new SymbolData("C.A.D");
1535 SymbolData sd5 = new SymbolData("C.B.D");
1536 sd1.setIsContinuation(false);
1537 sd2.setIsContinuation(false);
1538 sd3.setIsContinuation(false);
1539 sd4.setIsContinuation(false);
1540 sd5.setIsContinuation(false);
1541
1542 sd1.addInnerClass(sd4);
1543 sd4.setOuterData(sd1);
1544 sd2.addInnerClass(sd5);
1545 sd5.setOuterData(sd2);
1546 sd3.addInnerClass(sd1);
1547 sd1.setOuterData(sd3);
1548 sd3.addInnerClass(sd2);
1549 sd2.setOuterData(sd3);
1550
1551 assertEquals("Should return A.D", sd4, _btc.getSymbolData("A.D", sd3, NULL_LITERAL));
1552 assertEquals("Should return B.D", sd5, _btc.getSymbolData("B.D", sd3, NULL_LITERAL));
1553 assertEquals("Should return null", null, _btc.getSymbolData("D", sd3, NULL_LITERAL));
1554 assertEquals("Should return B", sd2, _btc.getSymbolData("B", sd1, NULL_LITERAL));
1555 assertEquals("Should return C.A", sd1, _btc.getSymbolData("A", sd5, NULL_LITERAL));
1556 //Test a class defined in the context of a method
1557 MethodData md = new MethodData("myMethod", _publicMav, new TypeParameter[0],
1558 SymbolData.INT_TYPE, new VariableData[0], new String[0], sdOuter,
1559 NULL_LITERAL);
1560 md.addInnerClass(sd3);
1561 assertEquals("Should return sd3", sd3, _btc.getSymbolData("C", md, NULL_LITERAL));
1562 }
1563
1564 public void testGetQualifiedClassName() {
1565 //first test when the package is empty:
1566 _btc._package = "";
1567 assertEquals("Should not change qualified name.", "simpson.Bart", _btc.getQualifiedClassName("simpson.Bart"));
1568 assertEquals("Should not change unqualified name.", "Lisa", _btc.getQualifiedClassName("Lisa"));
1569
1570 //now test when package is not empty.
1571 _btc._package="myPackage";
1572 assertEquals("Should not change properly packaged qualified name.", "myPackage.Snowball",
1573 _btc.getQualifiedClassName("myPackage.Snowball"));
1574 assertEquals("Should append package to front of not fully packaged name", "myPackage.simpson.Snowball",
1575 _btc.getQualifiedClassName("simpson.Snowball"));
1576 assertEquals("Should append package to front of unqualified class name.", "myPackage.Grandpa",
1577 _btc.getQualifiedClassName("Grandpa"));
1578 }
1579
1580 public void test_lookupMethodHelper() {
1581 VariableData[] vds =
1582 new VariableData[] { new VariableData("field1", _finalMav, SymbolData.DOUBLE_TYPE, true, null),
1583 new VariableData("field2", _finalMav, SymbolData.INT_TYPE, true, null) };
1584 VariableData[] vds2 =
1585 new VariableData[] { new VariableData("field3", _finalMav, SymbolData.DOUBLE_TYPE, true, null) };
1586 VariableData[] vds3 =
1587 new VariableData[] { new VariableData("field1", _finalMav, SymbolData.CHAR_TYPE, true, null) };
1588 InstanceData[] sds =
1589 new InstanceData[] { SymbolData.DOUBLE_TYPE.getInstanceData(), SymbolData.INT_TYPE.getInstanceData()};
1590 InstanceData[] sds2 = new InstanceData[] { SymbolData.DOUBLE_TYPE.getInstanceData() };
1591 InstanceData[] sds3 = new InstanceData[] { SymbolData.CHAR_TYPE.getInstanceData()};
1592 MethodData md =
1593 new MethodData("Bart", _publicMav, new TypeParameter[0], _sd1, vds, new String[0], _sd1, null);
1594 vds[0].setEnclosingData(md);
1595 vds[1].setEnclosingData(md);
1596 _sd1.addMethod(md);
1597 MethodData md2 =
1598 new MethodData("Homer", _publicMav, new TypeParameter[0], _sd2, vds2, new String[0], _sd2, null);
1599 vds2[0].setEnclosingData(md2);
1600 _sd2.addMethod(md2);
1601 _sd1.setSuperClass(_sd2);
1602 MethodData constructor =
1603 new MethodData("monkey", _publicMav, new TypeParameter[0], _sd1, vds3, new String[0], _sd1, null);
1604 vds3[0].setEnclosingData(constructor);
1605 _sd1.addMethod(constructor);
1606 assertEquals("Should return md.", md, _btc._lookupMethodHelper("Bart", _sd1, sds, null, false, _sd3));
1607 assertEquals("Should return md2.", md2, _btc._lookupMethodHelper("Homer", _sd1, sds2, null, false, _sd3));
1608 assertEquals("Should return constructor.", constructor, _btc._lookupMethodHelper("monkey", _sd1, sds3, null, true,
1609 _sd3));
1610 assertEquals("Should return null.", null, _btc._lookupMethodHelper("Homer", _sd1, sds2, null, true, _sd3));
1611 assertEquals("Should return null.", null, _btc._lookupMethodHelper("Lenny", _sd1, sds, null, false, _sd3));
1612 }
1613
1614 public void testLookupMethod() {
1615 VariableData[] vds =
1616 new VariableData[] { new VariableData("field1", _finalMav, SymbolData.DOUBLE_TYPE, true, null),
1617 new VariableData("field2", _finalMav, SymbolData.INT_TYPE, true, null) };
1618 InstanceData[] sds =
1619 new InstanceData[] { SymbolData.DOUBLE_TYPE.getInstanceData(), SymbolData.INT_TYPE.getInstanceData() };
1620 MethodData md = new MethodData("Bart", _publicMav, new TypeParameter[0], _sd1, vds, new String[0], _sd1, null);
1621 vds[0].setEnclosingData(md);
1622 vds[1].setEnclosingData(md);
1623 _sd1.addMethod(md);
1624
1625 assertEquals("Should return md.", md, _btc._lookupMethod("Bart", _sd1, sds, null, "Error message:", true, _sd3));
1626 assertEquals("Should be no errors.", 0, errors.size());
1627 assertEquals("Should return null.", null, _btc._lookupMethod("Lenny", _sd1, sds, null, "Lenny Error message: ",
1628 true, _sd3));
1629 assertEquals("Should be one error.", 1, errors.size());
1630 assertEquals("Newest error should have correct message", "Lenny Error message: Lenny(double, int).",
1631 errors.getLast().getFirst());
1632 }
1633
1634 public void testAreInSamePackage() {
1635 assertTrue("areInSamePackage with same qualified names", _btc._areInSamePackage(_sd1, _sd2));
1636 assertFalse("areInSamePackage with unqualified and qualified names", _btc._areInSamePackage(_sd1, _sd3));
1637 assertFalse("areInSamePackage with different qualified names", _btc._areInSamePackage(_sd1, _sd4));
1638 assertFalse("areInSamePackage with qualified and empty names", _btc._areInSamePackage(_sd4, _sd5));
1639 assertTrue("areInSamePackage with different unqualified names", _btc._areInSamePackage(_sd3, _sd6));
1640 }
1641
1642 public void testCheckAccessibility() {
1643 _sd6.setSuperClass(_sd3);
1644 _sd6.setOuterData(_sd2);
1645 _sd2.addInnerClass(_sd6);
1646 String sd3Name = _sd3.getName();
1647 String sd6Name = _sd6.getName();
1648
1649 // public
1650 assertTrue("checkAccess with public mav and same package",
1651 _btc.checkAccess(NULL_LITERAL, _publicMav, "fieldOfDreams", _sd1, _sd2, "field"));
1652 assertTrue("checkAccess with public mav and different packages",
1653 _btc.checkAccess(NULL_LITERAL, _publicMav, "fieldOfDreams", _sd1, _sd4, "field"));
1654 assertTrue("checkAccess with public mav and is outer class",
1655 _btc.checkAccess(NULL_LITERAL, _publicMav, "fieldOfDreams", _sd3, _sd6, "field"));
1656 assertTrue("checkAccess for a class and its outer class",
1657 _btc.checkAccess(NULL_LITERAL, _publicMav, sd6Name, _sd2, _sd6, "class"));
1658 assertTrue("checkAccess for a class and a class it is not related to",
1659 _btc.checkAccess(NULL_LITERAL, _publicMav, sd6Name, _sd2, _sd4, "class"));
1660
1661 // protected
1662 assertTrue("checkAccess with protected mav and same package",
1663 _btc.checkAccess(NULL_LITERAL, _protectedMav, "fieldOfDreams", _sd1, _sd2, "field"));
1664 assertFalse("checkAccess with protected mav and different package",
1665 _btc.checkAccess(NULL_LITERAL, _protectedMav, "fieldOfDreams", _sd1, _sd4, "field"));
1666 assertTrue("checkAccess with protected mav and is outer class",
1667 _btc.checkAccess(NULL_LITERAL, _protectedMav, "fieldOfDreams", _sd2, _sd6, "field"));
1668 assertTrue("checkAccess with protected mav and is super class",
1669 _btc.checkAccess(NULL_LITERAL, _protectedMav, "fieldOfDreams", _sd3, _sd6, "field"));
1670 assertTrue("checkAccess for a class and its outer class",
1671 _btc.checkAccess(NULL_LITERAL, _protectedMav, sd6Name, _sd2, _sd6, "class"));
1672 assertFalse("checkAccess for a class and a class it is not related to",
1673 _btc.checkAccess(NULL_LITERAL, _protectedMav, sd6Name, _sd2, _sd4, "class"));
1674
1675 // private
1676 assertFalse("checkAccess with private mav and same package",
1677 _btc.checkAccess(NULL_LITERAL, _privateMav, "fieldOfDreams", _sd1, _sd2, "field"));
1678 assertFalse("checkAccess with private mav and different package",
1679 _btc.checkAccess(NULL_LITERAL, _privateMav, "fieldOfDreams", _sd1, _sd4, "field"));
1680 assertTrue("checkAccess with private mav and is outer class",
1681 _btc.checkAccess(NULL_LITERAL, _privateMav, "fieldOfDreams", _sd2, _sd6, "field"));
1682 assertFalse("checkAccess with private mav and is super class",
1683 _btc.checkAccess(NULL_LITERAL, _privateMav, "fieldOfDreams", _sd3, _sd6, "field"));
1684 assertEquals("There should be 5 errors", 5, errors.size());
1685 assertEquals("The last error message should be correct",
1686 "The field fieldOfDreams in " + sd3Name + " is private and cannot be accessed from " + sd6Name,
1687 errors.getLast().getFirst());
1688 assertTrue("checkAccess with private mav and same file",
1689 _btc.checkAccess(NULL_LITERAL, _privateMav, "fieldOfDreams", _sd3, _sd3, "field"));
1690 assertTrue("checkAccess for a class and its outer class",
1691 _btc.checkAccess(NULL_LITERAL, _privateMav, sd6Name, _sd2, _sd6, "class"));
1692 assertFalse("checkAccess for a class and a class it is not related to",
1693 _btc.checkAccess(NULL_LITERAL, _privateMav, sd6Name, _sd2, _sd4, "class"));
1694
1695 // package
1696 assertTrue("checkAccess with package mav and same package",
1697 _btc.checkAccess(NULL_LITERAL, _packageMav, "fieldOfDreams", _sd1, _sd2, "field"));
1698 assertFalse("checkAccess with package mav and different package",
1699 _btc.checkAccess(NULL_LITERAL, _packageMav, "fieldOfDreams", _sd1, _sd4, "field"));
1700 assertTrue("checkAccess with package mav and is outer class",
1701 _btc.checkAccess(NULL_LITERAL, _packageMav, "fieldOfDreams", _sd2, _sd6, "field"));
1702 assertTrue("checkAccess with package mav and is super class",
1703 _btc.checkAccess(NULL_LITERAL, _packageMav, "fieldOfDreams", _sd3, _sd6, "field"));
1704 assertTrue("checkAccess for a class and its outer class",
1705 _btc.checkAccess(NULL_LITERAL, _packageMav, sd6Name, _sd2, _sd6, "class"));
1706 assertFalse("checkAccess for a class and a class it is not related to",
1707 _btc.checkAccess(NULL_LITERAL, _packageMav, sd6Name, _sd2, _sd4, "class"));
1708
1709 }
1710
1711 public void testGetFieldOrVariable() {
1712 _sd6.setSuperClass(_sd3);
1713 _sd6.setOuterData(_sd2);
1714 VariableData vd0 = new VariableData("field0", _privateMav, _sd1, true, _sd6);
1715 VariableData vd1 = new VariableData("field1", _publicMav, _sd1, true, _sd3);
1716 VariableData vd2 = new VariableData("field2", _privateMav, _sd2, true, _sd2);
1717 VariableData vd3 =
1718 new VariableData("variable1", new ModifiersAndVisibility(NONE, new String[0]), _sd3, true, _sd3);
1719 MethodData md = new MethodData("method1", _protectedMav, null, null, null, null, _sd3, null);
1720 md.addVar(vd3);
1721 _sd6.addVar(vd0);
1722 _sd3.addVar(vd1);
1723 _sd2.addVar(vd2);
1724 _sd3.addMethod(md);
1725 assertEquals("Should find field0", vd0, _btc.getFieldOrVariable("field0", _sd6, _sd6, NULL_LITERAL));
1726 assertEquals("Should find field1", vd1, _btc.getFieldOrVariable("field1", _sd6, _sd6, NULL_LITERAL));
1727 assertEquals("Should find field2", vd2, _btc.getFieldOrVariable("field2", _sd6, _sd6, NULL_LITERAL));
1728 assertEquals("Should not find field7", null, _btc.getFieldOrVariable("field7", _sd6, _sd6, NULL_LITERAL));
1729 assertEquals("Should find variable1", vd3, _btc.getFieldOrVariable("variable1", md, _sd3, NULL_LITERAL));
1730
1731 // test that passing in a list of variables to use will not recur (if called from the type checker)
1732 assertEquals("Should not find field1", null,
1733 _btc.getFieldOrVariable("field1", _sd6, _sd6, NULL_LITERAL, new LinkedList<VariableData>()));
1734 assertEquals("There should be no errors", 0, errors.size());
1735 }
1736
1737 public void test_addError() {
1738 LinkedList<Pair<String, JExpressionIF>> e = new LinkedList<Pair<String, JExpressionIF>>();
1739
1740 NullLiteral nl = NULL_LITERAL;
1741 NullLiteral nl2 = NULL_LITERAL;
1742
1743 e.addLast(new Pair<String,JExpressionIF>("Boy, is this an error!", nl));
1744 _addError("Boy, is this an error!", nl);
1745
1746 assertTrue("An error should have been added.", _errorAdded);
1747 assertEquals("The errors list should be correct.", e, errors);
1748
1749 e.addLast(new Pair<String,JExpressionIF>("Error again!", nl2));
1750 _addError("Error again!", nl2);
1751
1752 assertTrue("Another error should have been aded.", _errorAdded);
1753 assertEquals("The new errors list should be correct.", e, errors);
1754 }
1755
1756
1757 public void testCheckAbstractMethodsHelper() {
1758 /*
1759 * This should work.
1760 * _sd1 will be abstract.
1761 * _sd2 will be concrete and extend _sd1.
1762 * _sd3 will be abstract and extend _sd2.
1763 * _sd4 will be abstract and extend _sd3.
1764 * _sd5 will be concrete and extend _sd4.
1765 */
1766
1767 VariableData[] vds = new VariableData[] { new VariableData("field1", _finalMav, SymbolData.DOUBLE_TYPE, true, null),
1768 new VariableData("field2", _finalMav, SymbolData.INT_TYPE, true, null) };
1769 MethodData md1 = new MethodData("Abe", _abstractMav, new TypeParameter[0], _sd1,
1770 vds,
1771 new String[0],
1772 _sd1,
1773 null);
1774 vds[0].setEnclosingData(md1);
1775 vds[1].setEnclosingData(md1);
1776 _sd1.addMethod(md1);
1777 _sd1.setMav(_abstractMav);
1778 _sd1.setSuperClass(_sd6);
1779 _sd2.setSuperClass(_sd1);
1780 MethodData md2 = new MethodData("Homer", _abstractMav, new TypeParameter[0], _sd1,
1781 vds,
1782 new String[0],
1783 _sd3,
1784 null);
1785 MethodData md3 = new MethodData("Marge", _publicMav, new TypeParameter[0], _sd1,
1786 vds,
1787 new String[0],
1788 _sd3,
1789 null);
1790 MethodData md123 = new MethodData("Apu", _abstractMav, new TypeParameter[0], _sd1,
1791 vds,
1792 new String[0],
1793 _sd3,
1794 null);
1795 _sd3.addMethod(md2);
1796 _sd3.addMethod(md3);
1797 _sd3.addMethod(md123);
1798 _sd3.setMav(_abstractMav);
1799 _sd3.setSuperClass(_sd2);
1800 MethodData md4 = new MethodData("Bart", _abstractMav, new TypeParameter[0], _sd1,
1801 vds,
1802 new String[0],
1803 _sd4,
1804 null);
1805 MethodData md5 = new MethodData("Lisa", _abstractMav, new TypeParameter[0], _sd1,
1806 vds,
1807 new String[0],
1808 _sd4,
1809 null);
1810 MethodData md1234 = new MethodData("Apu", _publicMav, new TypeParameter[0], _sd1,
1811 vds, new String[0],
1812 _sd4,
1813 null);
1814 _sd4.addMethod(md4);
1815 _sd4.addMethod(md5);
1816 _sd4.addMethod(md1234);
1817 _sd4.setMav(_abstractMav);
1818 _sd4.setSuperClass(_sd3);
1819 MethodData md6 = new MethodData("Bart", _abstractMav, new TypeParameter[0], _sd1,
1820 vds,
1821 new String[0],
1822 _sd5,
1823 null);
1824 MethodData md7 = new MethodData("Homer", _abstractMav, new TypeParameter[0], _sd1,
1825 vds,
1826 new String[0],
1827 _sd5,
1828 null);
1829 MethodData md8 = new MethodData("Lisa", _abstractMav, new TypeParameter[0], _sd1,
1830 vds,
1831 new String[0],
1832 _sd5,
1833 null);
1834 _sd5.addMethod(md6);
1835 _sd5.addMethod(md7);
1836 _sd5.addMethod(md8);
1837 _sd5.setSuperClass(_sd4);
1838 LinkedList<MethodData> mds = _sd5.getMethods();
1839 // TODO: Check all enclosingDatas (interfaces too)
1840 _btc._checkAbstractMethodsHelper(_sd5, _sd5.getSuperClass(), mds, null);
1841 assertEquals("There should be no errors.", 0, errors.size());
1842 // must be declared abstract or must override the abstract method: " + md.getName()
1843 // Test one that doesn't work.
1844 mds = _sd2.getMethods();
1845 _btc._checkAbstractMethodsHelper(_sd2, _sd2.getSuperClass(), mds, null);
1846 assertEquals("There should be one error.", 1, errors.size());
1847 assertEquals("The error message should be correct.",
1848 "i.like.giraffe must be declared abstract or must override the abstract method: Abe(double, int) in "
1849 + _sd2.getSuperClass().getName(),
1850 errors.get(0).getFirst());
1851
1852 //check that an interface's abstract methods are recognized.
1853 _sd3.setInterface(true);
1854 _sd3.setMethods(new LinkedList<MethodData>());
1855 _sd3.addMethod(md7);
1856 _sd2.addEnclosingData(_sd3);
1857 SymbolData sd = symbolTable.get("java.lang.Object");
1858 // sd.setIsContinuation(false);
1859 _sd2.setSuperClass(sd);
1860 _btc._checkAbstractMethodsHelper(_sd2, _sd3, mds, null);
1861 assertEquals("There should be two errors.", 2, errors.size());
1862 assertEquals("The error message should be correct.",
1863 "i.like.giraffe must be declared abstract or must override the abstract method: Homer(double, int) in "
1864 + _sd3.getName(), errors.get(1).getFirst());
1865 }
1866
1867 public void testForPrimitiveType() {
1868 assertEquals("Should return SymbolData.INT_TYPE.",
1869 SymbolData.INT_TYPE,
1870 _btc.forPrimitiveType(new PrimitiveType(NONE, "int")));
1871 try {
1872 _btc.forPrimitiveType(new PrimitiveType(NONE, "Maggie"));
1873 fail("Should have thrown an exception.");
1874 }
1875 catch(RuntimeException re) {
1876 }
1877 }
1878
1879 public void test_isAssignableFrom() {
1880 assertTrue("Should be assignable.",
1881 _btc._isAssignableFrom(SymbolData.DOUBLE_TYPE, symbolTable.get("java.lang.Double")));
1882 assertFalse("Should not be assignable.",
1883 _btc._isAssignableFrom(symbolTable.get("java.lang.Double"), SymbolData.FLOAT_TYPE));
1884 assertTrue("Should be assignable.",
1885 _btc._isAssignableFrom(SymbolData.DOUBLE_TYPE, symbolTable.get("java.lang.Long")));
1886 assertFalse("Should not be assignable.",
1887 _btc._isAssignableFrom(symbolTable.get("java.lang.Double"), SymbolData.INT_TYPE));
1888 assertTrue("Should be assignable.",
1889 _btc._isAssignableFrom(SymbolData.DOUBLE_TYPE, symbolTable.get("java.lang.Short")));
1890 assertFalse("Should not be assignable.", _btc._isAssignableFrom(symbolTable.get("java.lang.Double"), symbolTable.get("java.lang.Character")));
1891 assertTrue("Should be assignable.", _btc._isAssignableFrom(SymbolData.DOUBLE_TYPE, SymbolData.BYTE_TYPE));
1892 assertTrue("Should be assignable.", _btc._isAssignableFrom(symbolTable.get("java.lang.Integer"), symbolTable.get("java.lang.Integer")));
1893 assertFalse("Should not be assignable.", _btc._isAssignableFrom(symbolTable.get("java.lang.Integer"), symbolTable.get("java.lang.Character")));
1894 assertTrue("Should be assignable.", _btc._isAssignableFrom(symbolTable.get("java.lang.Character"), SymbolData.CHAR_TYPE));
1895 assertFalse("Should not be assignable.", _btc._isAssignableFrom(symbolTable.get("java.lang.Boolean"), SymbolData.INT_TYPE));
1896
1897 assertTrue("Should be assignable", _btc._isAssignableFrom(symbolTable.get("java.lang.Object"), SymbolData.INT_TYPE));
1898
1899 _sd2.setSuperClass(_sd1);
1900 assertTrue("Should be assignable.", _btc._isAssignableFrom(_sd1, _sd1));
1901 assertTrue("Should be assignable.", _btc._isAssignableFrom(_sd1, _sd2));
1902
1903 //test 1.5/1.4 auto-boxing and unboxing
1904 assertFalse("Should not be assignable.", _btc._isAssignableFrom(symbolTable.get("java.lang.Double"), SymbolData.INT_TYPE));
1905 assertTrue("Should be assignable.", _btc._isAssignableFrom(SymbolData.DOUBLE_TYPE, symbolTable.get("java.lang.Short")));
1906 assertFalse("Should not be assignable.", _btc._isAssignableFrom(symbolTable.get("java.lang.Double"), symbolTable.get("java.lang.Character")));
1907 assertTrue("Should be assignable.", _btc._isAssignableFrom(SymbolData.DOUBLE_TYPE, SymbolData.BYTE_TYPE));
1908 assertTrue("Should be assignable.", _btc._isAssignableFrom(symbolTable.get("java.lang.Integer"), symbolTable.get("java.lang.Integer")));
1909 assertFalse("Should not be assignable.", _btc._isAssignableFrom(symbolTable.get("java.lang.Integer"), symbolTable.get("java.lang.Character")));
1910 assertTrue("Should be assignable.", _btc._isAssignableFrom(symbolTable.get("java.lang.Object"), SymbolData.INT_TYPE));
1911
1912 LanguageLevelVisitor llv =
1913 new LanguageLevelVisitor(_btc._file,
1914 _btc._package,
1915 null, // enclosingClassName for top level traversal
1916 _btc._importedFiles,
1917 _btc._importedPackages,
1918 new HashSet<String>(),
1919 new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
1920 new LinkedList<Command>());
1921
1922 // LanguageLevelConverter.symbolTable = llv.symbolTable = _btc.symbolTable;
1923 LanguageLevelConverter._newSDs.clear();
1924
1925 SourceInfo si = NONE;
1926
1927 ArrayData intArray = new ArrayData(SymbolData.INT_TYPE, llv, si);
1928 assertTrue("Should be able to assign an array to Object",
1929 _btc._isAssignableFrom(symbolTable.get("java.lang.Object"), intArray));
1930
1931 ArrayData doubleArray = new ArrayData(SymbolData.DOUBLE_TYPE, llv, si);
1932 assertFalse("Should not be able to assign an int[] to a double[]",
1933 _btc._isAssignableFrom(doubleArray, intArray));
1934
1935 ArrayData integerArray = new ArrayData(symbolTable.get("java.lang.Integer"), llv, si);
1936 assertFalse("Should not be able to assign an array of ints to an array of Integers",
1937 _btc._isAssignableFrom(integerArray, intArray));
1938 assertFalse("Should not be able to assign an array of Integers to an array of ints",
1939 _btc._isAssignableFrom(intArray, integerArray));
1940
1941 assertTrue("Should be able to assign an array to an interface of java.io.Serializable",
1942 _btc._isAssignableFrom(symbolTable.get("java.io.Serializable"), integerArray));
1943
1944 LanguageLevelConverter.OPT = new Options(JavaVersion.JAVA_5, EmptyIterable.<File>make());
1945 assertFalse("Should not be assignable.",
1946 _btc._isAssignableFrom(symbolTable.get("java.lang.Double"), SymbolData.INT_TYPE));
1947 assertFalse("Should not be assignable.",
1948 _btc._isAssignableFrom(symbolTable.get("java.lang.Double"), symbolTable.get("java.lang.Character")));
1949 assertTrue("Should be assignable.", _btc._isAssignableFrom(SymbolData.DOUBLE_TYPE, SymbolData.BYTE_TYPE));
1950 assertTrue("Should be assignable.",
1951 _btc._isAssignableFrom(SymbolData.DOUBLE_TYPE, symbolTable.get("java.lang.Short")));
1952 assertTrue("Should be assignable.",
1953 _btc._isAssignableFrom(symbolTable.get("java.lang.Integer"), symbolTable.get("java.lang.Integer")));
1954 assertFalse("Should not be assignable.",
1955 _btc._isAssignableFrom(symbolTable.get("java.lang.Integer"), symbolTable.get("java.lang.Character")));
1956 assertTrue("Should not assignable.",
1957 _btc._isAssignableFrom(SymbolData.INT_TYPE, symbolTable.get("java.lang.Character")));
1958 assertTrue("Should be assignable.",
1959 _btc._isAssignableFrom(symbolTable.get("java.lang.Object"), SymbolData.INT_TYPE));
1960 assertTrue("Should be able to assign an array to Object",
1961 _btc._isAssignableFrom(symbolTable.get("java.lang.Object"), intArray));
1962
1963
1964 //check that classes are know to be subclasses of their interfaces.
1965 SymbolData myData = new SymbolData("yes");
1966 SymbolData yourData = new SymbolData("interface");
1967 yourData.setInterface(true);
1968 myData.setIsContinuation(false);
1969 myData.addInterface(yourData);
1970 yourData.setIsContinuation(false);
1971 assertTrue("Should be assignable", _btc._isAssignableFrom(yourData, myData));
1972 }
1973
1974
1975 public void test_isAssignableFromWithoutAutoboxing() {
1976 assertTrue("Should be assignable.",
1977 _btc._isAssignableFromWithoutAutoboxing(SymbolData.DOUBLE_TYPE, SymbolData.DOUBLE_TYPE));
1978 assertTrue("Should be assignable.",
1979 _btc._isAssignableFromWithoutAutoboxing(SymbolData.DOUBLE_TYPE, SymbolData.FLOAT_TYPE));
1980 assertTrue("Should be assignable.",
1981 _btc._isAssignableFromWithoutAutoboxing(SymbolData.DOUBLE_TYPE, SymbolData.LONG_TYPE));
1982 assertTrue("Should be assignable.",
1983 _btc._isAssignableFromWithoutAutoboxing(SymbolData.DOUBLE_TYPE, SymbolData.INT_TYPE));
1984 assertTrue("Should be assignable.",
1985 _btc._isAssignableFromWithoutAutoboxing(SymbolData.DOUBLE_TYPE, SymbolData.SHORT_TYPE));
1986 assertTrue("Should be assignable.",
1987 _btc._isAssignableFromWithoutAutoboxing(SymbolData.DOUBLE_TYPE, SymbolData.CHAR_TYPE));
1988 assertTrue("Should be assignable.",
1989 _btc._isAssignableFromWithoutAutoboxing(SymbolData.DOUBLE_TYPE, SymbolData.BYTE_TYPE));
1990 assertTrue("Should be assignable.",
1991 _btc._isAssignableFromWithoutAutoboxing(SymbolData.INT_TYPE, SymbolData.INT_TYPE));
1992 assertTrue("Should be assignable.",
1993 _btc._isAssignableFromWithoutAutoboxing(SymbolData.INT_TYPE, SymbolData.CHAR_TYPE));
1994 assertTrue("Should be assignable.",
1995 _btc._isAssignableFromWithoutAutoboxing(SymbolData.CHAR_TYPE, SymbolData.CHAR_TYPE));
1996
1997 _sd2.setSuperClass(_sd1);
1998 assertTrue("Should be assignable.", _btc._isAssignableFromWithoutAutoboxing(_sd1, _sd1));
1999 assertTrue("Should be assignable.", _btc._isAssignableFromWithoutAutoboxing(_sd1, _sd2));
2000 }
2001
2002 public void testCheckForCyclicInheritance() {
2003 _sd1.setSuperClass(_sd2);
2004 _sd2.addInterface(_sd3);
2005 _sd2.setSuperClass(new SymbolData("java.lang.String"));
2006 _sd3.addInterface(symbolTable.get("java.lang.Object"));
2007 _sd1.addInnerClass(_sd4);
2008
2009 //no cyclic inheritance
2010 InterfaceDef nl =
2011 new InterfaceDef(NONE, _publicMav, new Word(NONE, "name"), new TypeParameter[0], new ReferenceType[0],
2012 new BracedBody(NONE, new BodyItemI[0]));
2013 assertFalse("Should not be cyclic inheritance",
2014 _btc.checkForCyclicInheritance(_sd1, new LinkedList<SymbolData>(), nl));
2015
2016 //if you and your super interface implement the same interface, it's okay
2017 _sd4.addInterface(_sd5);
2018 _sd4.addInterface(_sd6);
2019 _sd5.addInterface(_sd6);
2020 assertFalse("Should not be cyclic inheritance",
2021 _btc.checkForCyclicInheritance(_sd4, new LinkedList<SymbolData>(), nl));
2022
2023 _sd3.addInterface(_sd2);
2024 //cyclic inheritance
2025 assertTrue("Should be cyclic inheritance",
2026 _btc.checkForCyclicInheritance(_sd1, new LinkedList<SymbolData>(), nl));
2027 assertEquals("There should be one error", 1, errors.size());
2028 assertEquals("The error message should be correct", "Cyclic inheritance involving " + _sd2.getName(),
2029 errors.get(0).getFirst());
2030
2031 //if your super class implements your inner class, there is cyclic inheritance
2032 ArrayList<SymbolData> temp = _sd3.getInterfaces();
2033 _sd3.getInterfaces().remove(_sd2);
2034 _sd3.setInterfaces(temp);
2035 _sd2.setSuperClass(_sd6);
2036 _sd6.setSuperClass(_sd4);
2037 _sd4.setSuperClass(symbolTable.get("java.lang.Object"));
2038 assertTrue("Should be cyclic inheritance", _btc.checkForCyclicInheritance(_sd1, new LinkedList<SymbolData>(), nl));
2039 assertEquals("Should now be 2 errors", 2, errors.size());
2040 assertEquals("The error message should be correct", "Cyclic inheritance involving " + _sd4.getName(),
2041 errors.get(1).getFirst());
2042 }
2043
2044 public void testForClassDef() {
2045
2046 ClassDef cd =
2047 new ClassDef(NONE, _publicMav, new Word(NONE, "Lisa"),
2048 new TypeParameter[0],
2049 new ClassOrInterfaceType(NONE, "java.lang.Object", new Type[0]), new ReferenceType[0],
2050 new BracedBody(NONE, new BodyItemI[0]));
2051
2052 //Test that no cyclic inheritance goes okay
2053 SymbolData Lisa = new SymbolData("Lisa");
2054 Lisa.setSuperClass(symbolTable.get("java.lang.Object"));
2055
2056 Lisa.setIsContinuation(false);
2057 Lisa.setMav(_publicMav);
2058 _btc._file=new File("Lisa.dj1");
2059 _btc.symbolTable.put("Lisa", Lisa);
2060 TypeData result = cd.visit(_btc);
2061
2062
2063 assertEquals("There should be no errors", 0, errors.size());
2064
2065 //Test that cyclic inheritance throws an error
2066 Lisa.setSuperClass(Lisa);
2067 result = cd.visit(_btc);
2068
2069 assertEquals("There should be one error", 1, errors.size());
2070 assertEquals("Error message should be correct", "Cyclic inheritance involving Lisa", errors.get(0).getFirst());
2071
2072 //Test that if you extend a final class, an error is thrown
2073 ClassDef cd2 =
2074 new ClassDef(NONE, _publicMav, new Word(NONE, "Me"),
2075 new TypeParameter[0], new ClassOrInterfaceType(NONE, "Parent", new Type[0]), new ReferenceType[0],
2076 new BracedBody(NONE, new BodyItemI[0]));
2077 SymbolData parent = new SymbolData("Parent");
2078 parent.setMav(_finalMav);
2079 parent.setSuperClass(symbolTable.get("java.lang.Object"));
2080 _btc.symbolTable.put("Parent", parent);
2081
2082 SymbolData me = new SymbolData("Me");
2083
2084 me.setSuperClass(parent);
2085 me.setIsContinuation(false);
2086 _btc.symbolTable.put("Me", me);
2087
2088 result = cd2.visit(_btc);
2089
2090
2091 assertEquals("There should be 2 errors", 2, errors.size());
2092 assertEquals("2nd Error message should be correct", "Class Me cannot extend the final class Parent",
2093 errors.get(1).getFirst());
2094
2095
2096 //Test that if you extend a class you cannot see, an error is thrown
2097 ClassDef cd3 =
2098 new ClassDef(NONE, _publicMav, new Word(NONE, "somewhereElse.Lisa"),
2099 new TypeParameter[0], new ClassOrInterfaceType(NONE, "java.lang.Object", new Type[0]),
2100 new ReferenceType[0],
2101 new BracedBody(NONE, new BodyItemI[0]));
2102
2103 Lisa = new SymbolData("somewhereElse.Lisa");
2104 Lisa.setSuperClass(new SymbolData("hungry"));
2105 Lisa.setPackage("somewhereElse");
2106 Lisa.setIsContinuation(false);
2107 Lisa.getSuperClass().setIsContinuation(false);
2108
2109 _btc.symbolTable.put("somewhereElse.Lisa", Lisa);
2110 result = cd3.visit(_btc);
2111
2112 assertEquals("There should be three errors", 3, errors.size());
2113 assertEquals("3rd error message should be correct",
2114 "The class hungry is package protected because there is no access specifier and cannot be "
2115 + "accessed from somewhereElse.Lisa",
2116 errors.get(2).getFirst());
2117
2118 //Test that if you implement an interface you cannot see, an error is thrown
2119 SymbolData superI = new SymbolData("superI");
2120 superI.setInterface(true);
2121 superI.setIsContinuation(false);
2122 Lisa.setSuperClass(new SymbolData("super", _publicMav, new TypeParameter[0], null, new ArrayList<SymbolData>(), null));
2123
2124 Lisa.addInterface(superI);
2125 _btc.symbolTable.put("superI", superI);
2126
2127 ClassDef cd4 =
2128 new ClassDef(NONE, _publicMav, new Word(NONE, "somewhereElse.Lisa"),
2129 new TypeParameter[0], new TypeVariable(NONE, "super"),
2130 new ReferenceType[] {new ClassOrInterfaceType(NONE, "superI", new Type[0])},
2131 new BracedBody(NONE, new BodyItemI[0]));
2132
2133
2134
2135 cd4.visit(_btc);
2136 assertEquals("There should be four errors", 4, errors.size());
2137 assertEquals("The 4th error message should be correct",
2138 "The interface superI is package protected because there is no access specifier and cannot be "
2139 + "accessed from somewhereElse.Lisa",
2140 errors.get(3).getFirst());
2141
2142 // Test that if a class has a final field and a constructor that sets the value, it won't throw an error
2143 VariableDeclarator vdec =
2144 new UninitializedVariableDeclarator(NONE, new PrimitiveType(NONE, "int"), new Word(NONE, "i"));
2145 VariableDeclaration vd = new VariableDeclaration(NONE, _finalMav, new VariableDeclarator[] {vdec});
2146 ExpressionStatement se =
2147 new ExpressionStatement(NONE,
2148 new SimpleAssignmentExpression(NONE, new SimpleNameReference(NONE, new Word(NONE, "i")),
2149 new IntegerLiteral(NONE, 1)));
2150 BracedBody cbb = new BracedBody(NONE, new BodyItemI[] {se});
2151 ConstructorDef consD =
2152 new ConstructorDef(NONE, new Word(NONE, "Jimes"), _publicMav, new FormalParameter[0], new ReferenceType[0], cbb);
2153 BracedBody b = new BracedBody(NONE, new BodyItemI[] {vd, consD});
2154 ClassDef cd5 =
2155 new ClassDef(NONE, _publicMav, new Word(NONE, "Jimes"),
2156 new TypeParameter[0], new ClassOrInterfaceType(NONE, "java.lang.Object", new Type[0]),
2157 new ReferenceType[0], new BracedBody(NONE, new BodyItemI[] {vd, consD}));
2158
2159 SymbolData sd = new SymbolData("Jimes");
2160 VariableData vData = new VariableData("i", _finalMav, SymbolData.INT_TYPE, false, sd);
2161 sd.setIsContinuation(false);
2162 sd.addVar(vData);
2163 SymbolData sd2 = symbolTable.get("java.lang.Object");
2164 // sd2.setIsContinuation(false);
2165 // sd2.setMav(_publicMav);
2166 // sd2.setPackage("java.lang");
2167 MethodData objMd =
2168 new MethodData("java.lang.Object", _publicMav, new TypeParameter[0], sd2, new VariableData[0], new String[0],
2169 sd2, cd);
2170 sd2.addMethod(objMd);
2171
2172 sd.setSuperClass(sd2);
2173 symbolTable.put("Jimes", sd);
2174 MethodData md = new MethodData("Jimes", _publicMav, new TypeParameter[0], sd, new VariableData[0], new String[0],
2175 sd, cd);
2176 sd.addMethod(md);
2177
2178 cd5.visit(_btc);
2179 assertEquals("There should still be four errors", 4, errors.size());
2180
2181 // Test that if a class has a final field, that if there are no constructors, an error is thrown since the value
2182 // of the field cannot be set.
2183 vData.lostValue();
2184 b = new BracedBody(NONE, new BodyItemI[] {vd});
2185 cd5 = new ClassDef(NONE, _publicMav, new Word(NONE, "Jimes"), new TypeParameter[0],
2186 new ClassOrInterfaceType(NONE, "java.lang.Object", new Type[0]),
2187 new ReferenceType[0], b);
2188
2189 cd5.visit(_btc);
2190 assertEquals("There should be 5 errors now", 5, errors.size());
2191 assertEquals("The error message should be correct", "The final field i has not been initialized",
2192 errors.get(4).getFirst());
2193
2194 //Test that if the class implements java.lang.Runnable, then an error is thrown.
2195 ClassDef cd6 =
2196 new ClassDef(NONE, _publicMav, new Word(NONE, "JimesH"),
2197 new TypeParameter[0],
2198 new ClassOrInterfaceType(NONE, "java.lang.Object", new Type[0]),
2199 new ReferenceType[] {new ClassOrInterfaceType(NONE, "java.lang.Runnable", new Type[0])},
2200 new BracedBody(NONE, new BodyItemI[0]));
2201 sd = new SymbolData("JimesH");
2202 sd.setIsContinuation(false);
2203
2204 symbolTable.clear();
2205 SymbolData runnableSd = new SymbolData("java.lang.Runnable");
2206 runnableSd.setMav(_publicMav);
2207 runnableSd.setIsContinuation(false);
2208 runnableSd.setPackage("java.lang");
2209 runnableSd.setInterface(true);
2210 sd.addInterface(runnableSd);
2211 symbolTable.put("JimesH", sd);
2212 symbolTable.remove("java.lang.Runnable");
2213 symbolTable.put("java.lang.Runnable", runnableSd);
2214
2215 cd6.visit(_btc);
2216 assertEquals("There should be 6 errors now", 6, errors.size());
2217 assertEquals("The error message should be correct",
2218 "JimesH implements the Runnable interface, which is not allowed at any language level",
2219 errors.get(5).getFirst());
2220
2221 //Test that if a class implements another class, an error is thrown
2222 ClassDef cd7 =
2223 new ClassDef(NONE, _publicMav, new Word(NONE, "Hspia"),
2224 new TypeParameter[0], new ClassOrInterfaceType(NONE, "superSD", new Type[0]),
2225 new ReferenceType[] {new ClassOrInterfaceType(NONE, "java.lang.String", new Type[0])},
2226 new BracedBody(NONE, new BodyItemI[0]));
2227 sd = new SymbolData("Hspia");
2228 sd.setIsContinuation(false);
2229
2230 symbolTable.clear();
2231 SymbolData stringSd = new SymbolData("java.lang.String");
2232 stringSd.setMav(_publicMav);
2233 stringSd.setIsContinuation(false);
2234 stringSd.setPackage("java.lang");
2235 stringSd.setInterface(false);
2236 sd.addInterface(stringSd);
2237 symbolTable.put("Hspia", sd);
2238 symbolTable.put("java.lang.String", stringSd);
2239
2240 cd7.visit(_btc);
2241 assertEquals("There should be 7 errors now", 7, errors.size());
2242 assertEquals("The error message should be correct",
2243 "java.lang.String is not an interface and thus cannot appear after the keyword 'implements' here."
2244 + " Perhaps you meant to say 'extends'?" ,
2245 errors.get(6).getFirst());
2246
2247
2248 //Test that if a class extends an interface, an error is thrown
2249
2250 stringSd.setInterface(true);
2251 SymbolData superSD = new SymbolData("SuperSD");
2252 superSD.setInterface(true);
2253 sd.setSuperClass(superSD);
2254 sd.setInterfaces(new ArrayList<SymbolData>());
2255 symbolTable.put("superSD", superSD);
2256 cd7.visit(_btc);
2257 assertEquals("There should be 8 errors now", 8, errors.size());
2258 assertEquals("The error message should be correct",
2259 "SuperSD is an interface and thus cannot appear after the keyword 'extends' here." +
2260 " Perhaps you meant to say 'implements'?", errors.get(7).getFirst());
2261
2262 //Test that if a public class is not in a file of the same name, an error is thrown
2263 superSD.setInterface(false);
2264 sd.addModifier("public");
2265 cd7.visit(_btc);
2266 assertEquals("There should now be 9 errors", 9, errors.size());
2267 assertEquals("The error message should be correct",
2268 "Hspia is public thus must be defined in a file with the same name.", errors.get(8).getFirst());
2269
2270 //Test that if a public class is in a file of the same name, no error is thrown.
2271 _btc._file = new File("Hspia.dj1");
2272 cd7.visit(_btc);
2273 assertEquals("There should still be just 9 errors", 9, errors.size());
2274
2275 //Test that if a class that is not public extends test case, an error is thrown.
2276 //Also check that if a test class doesn't have any test methods, an error is thrown.
2277 sd.setMav(_privateMav);
2278 symbolTable.remove("Hspia");
2279 symbolTable.put("Hspia", sd);
2280
2281 SymbolData testCase = defineTestCaseClass();
2282 sd.setSuperClass(testCase);
2283
2284 cd7.visit(_btc);
2285
2286 assertEquals("There should now be 11 errors", 11, errors.size());
2287 assertEquals("The tenth error message should be correct",
2288 "Hspia extends TestCase and thus must be explicitly declared public" ,
2289 errors.get(9).getFirst());
2290 assertEquals("The eleventh error message should be correct",
2291 "Class Hspia does not have any valid test methods. Test methods must be declared public, " +
2292 "must return void, and must start with the word \"test\"" ,
2293 errors.get(10).getFirst());
2294
2295 //Test that if a class that is not public extends test case, an error is thrown.
2296 //Also check that if a test class doesn't have any test methods, an error is thrown.
2297 _btc._file = new File("Hspia.dj0");
2298 sd.setMav(_publicMav);
2299 symbolTable.remove("Hspia");
2300 symbolTable.put("Hspia", sd);
2301
2302 // SymbolData testCase = defineTestCase();
2303 // symbolTable.put("junit.framework.TestCase", testCase);
2304 // sd.setSuperClass(testCase);
2305
2306 cd7.visit(_btc);
2307
2308 assertEquals("There should still be 11 errors", 11, errors.size()); // Generated duplicate error message
2309 assertEquals("The 12th error message should be correct",
2310 "Class Hspia does not have any valid test methods. Test methods must be declared public, " +
2311 "must return void, and must start with the word \"test\"" ,
2312 errors.get(10).getFirst());
2313
2314 }
2315
2316 public void testForInterfaceDef() {
2317 InterfaceDef id = new InterfaceDef(NONE, _publicMav, new Word(NONE, "Lisa"),
2318 new TypeParameter[0],
2319 new ReferenceType[] {new ClassOrInterfaceType(NONE, "superI", new Type[0])},
2320 new BracedBody(NONE, new BodyItemI[0]));
2321
2322 //Test that no cyclic inheritance goes okay
2323 SymbolData Lisa = new SymbolData("Lisa");
2324 Lisa.setInterface(true);
2325 SymbolData superI = new SymbolData("superI");
2326 superI.setInterface(true);
2327 Lisa.addInterface(superI);
2328 Lisa.setIsContinuation(false);
2329 superI.setIsContinuation(false);
2330 _btc.symbolTable.put("Lisa", Lisa);
2331 _btc.symbolTable.put("superI", superI);
2332
2333 TypeData result = id.visit(_btc);
2334
2335
2336 assertEquals("There should be no errors", 0, errors.size());
2337
2338 //Test that cyclic inheritance throws an error
2339 Lisa.addInterface(Lisa);
2340 result = id.visit(_btc);
2341
2342 assertEquals("There should be one error", 1, errors.size());
2343 assertEquals("Error message should be correct", "Cyclic inheritance involving Lisa", errors.get(0).getFirst());
2344
2345 //Test that if you implement an interface you cannot see, an error is thrown:
2346 InterfaceDef id2 = new InterfaceDef(NONE, _publicMav, new Word(NONE, "somewhereElse.Lisa"),
2347 new TypeParameter[0],
2348 new ReferenceType[] {new ClassOrInterfaceType(NONE, "superI", new Type[0])},
2349 new BracedBody(NONE, new BodyItemI[0]));
2350 Lisa = new SymbolData("somewhereElse.Lisa");
2351 Lisa.setIsContinuation(false);
2352 Lisa.addInterface(superI);
2353 Lisa.setPackage("somewhereElse");
2354 Lisa.setInterface(true);
2355 _btc.symbolTable.put("somewhereElse.Lisa", Lisa);
2356 superI.setMav(_privateMav);
2357
2358 id2.visit(_btc);
2359
2360 assertEquals("There should be 2 errors", 2, errors.size());
2361 assertEquals("The error message should be correct",
2362 "The interface superI in superI is private and cannot be accessed from somewhereElse.Lisa",
2363 errors.get(1).getFirst());
2364
2365 /* The Runnable restriction has been dropped. */
2366 // //Test that if the interface extends java.lang.Runnable, then an error is thrown.
2367 // InterfaceDef id3 =
2368 // new InterfaceDef(NONE, _publicMav, new Word(NONE, "JimesH"),
2369 // new TypeParameter[0],
2370 // new ReferenceType[] {new ClassOrInterfaceType(NONE, "java.lang.Runnable", new Type[0])},
2371 // new BracedBody(NONE, new BodyItemI[0]));
2372 // SymbolData sd = new SymbolData("JimesH");
2373 // sd.setIsContinuation(false);
2374 // sd.setInterface(true);
2375 //
2376 // symbolTable.clear();
2377 // SymbolData runnableSd = new SymbolData("java.lang.Runnable");
2378 // runnableSd.setMav(_publicMav);
2379 // runnableSd.setIsContinuation(false);
2380 // runnableSd.setPackage("java.lang");
2381 // runnableSd.setInterface(true);
2382 // sd.addInterface(runnableSd);
2383 // symbolTable.put("JimesH", sd);
2384 // symbolTable.remove("java.lang.Runnable");
2385 // symbolTable.put("java.lang.Runnable", runnableSd);
2386 //
2387 // id3.visit(_btc);
2388 // assertEquals("There should be 3 errors now", 3, errors.size());
2389 // assertEquals("The error message should be correct",
2390 // "JimesH extends the Runnable interface, which is not allowed at any language level",
2391 // errors.get(2).getFirst());
2392
2393 // Test that an error is thrown if you implement a class
2394
2395 InterfaceDef id4 =
2396 new InterfaceDef(NONE, _publicMav, new Word(NONE, "Bart"),
2397 new TypeParameter[0],
2398 new ReferenceType[] {new ClassOrInterfaceType(NONE, "superC", new Type[0])},
2399 new BracedBody(NONE, new BodyItemI[0]));
2400 // Test that no cyclic inheritance goes okay
2401 SymbolData me = new SymbolData("Bart");
2402 me.setInterface(true);
2403 SymbolData superC = new SymbolData("superC");
2404 superC.setInterface(false);
2405 //Lisa.setSuperClass(superI);
2406 me.addInterface(superC);
2407 me.setIsContinuation(false);
2408 superC.setIsContinuation(false);
2409 _btc.symbolTable.put("Bart", me);
2410 _btc.symbolTable.put("superC", superC);
2411
2412 result = id4.visit(_btc);
2413
2414
2415 assertEquals("There should be 3 errors now ", 3, errors.size());
2416 assertEquals("The error message should be correct",
2417 "superC is not an interface and thus cannot appear after the keyword 'extends' here",
2418 errors.getLast().getFirst());
2419
2420 // Test that no error is thrown if you implement an interface
2421 superC.setInterface(true);
2422 result = id4.visit(_btc);
2423 assertEquals("There should still just be 3 errors", 3, errors.size());
2424
2425 // Test that if a public interface is in a file of the wrong name, an error is thrown.
2426 me.addModifier("public");
2427 result = id4.visit(_btc);
2428 assertEquals("There should be 4 errorrs", 4, errors.size());
2429 assertEquals("The error message should be correct",
2430 "Bart is public thus must be defined in a file with the same name.",
2431 errors.getLast().getFirst());
2432
2433 // Test that if a public interface is in a file of the right name, no error is thrown.
2434 _btc._file = new File("Bart.dj1");
2435 result = id4.visit(_btc);
2436 assertEquals("There should still just be 4 errors", 4, errors.size());
2437 }
2438
2439 public void testForClassImportStatement() {
2440 Word[] words = new Word[] {
2441 new Word(NONE, "alpha"),
2442 new Word(NONE, "beta")};
2443 CompoundWord cw = new CompoundWord(NONE, words);
2444 ClassImportStatement cis = new ClassImportStatement(NONE, cw);
2445 SymbolData sd = new SymbolData("alpha.beta");
2446 //Test one that will work
2447 sd.setPackage("alpha");
2448 sd.setIsContinuation(false);
2449 sd.setMav(_publicMav);
2450 symbolTable.put("alpha.beta", sd);
2451
2452 cis.visit(_btc);
2453 assertEquals("There should be no errors", 0, errors.size());
2454
2455 //Test one that does not work (file is not in the right package)
2456 sd.setPackage("");
2457 cis.visit(_btc);
2458
2459 assertEquals("There should be 1 error", 1, errors.size());
2460 assertEquals("The error message should be correct",
2461 "The class alpha.beta is not in the right package. Perhaps you meant to package it?",
2462 errors.get(0).getFirst());
2463 }
2464
2465 public void testForPackageStatement() {
2466 Word[] badWords = new Word[] {
2467 new Word(NONE, "java"),
2468 new Word(NONE, "lang"),
2469 new Word(NONE, "Object")};
2470 Word[] okWords = new Word[] { new Word(NONE, "java"), new Word(NONE, "lang")};
2471
2472 PackageStatement badPackage =
2473 new PackageStatement(NONE, new CompoundWord(NONE, badWords));
2474 PackageStatement okPackage =
2475 new PackageStatement(NONE, new CompoundWord(NONE, okWords));
2476
2477 // SymbolData object = new SymbolData("java.lang.Object");
2478 // object.setPackage("java.lang");
2479 // object.setIsContinuation(false);
2480 // symbolTable.put("java.lang.Object", object);
2481
2482 okPackage.visit(_btc);
2483 assertEquals("Should be 0 errors", 0, errors.size());
2484
2485 badPackage.visit(_btc);
2486 assertEquals("Should be 1 error", 1, errors.size());
2487 assertEquals("Error message should be correct",
2488 "java.lang.Object is not a allowable package name, because it conflicts with a class you have"
2489 + " already defined",
2490 errors.getLast().getFirst());
2491 }
2492
2493 public void testAutoBoxingAndUnboxing() {
2494 //Test that autoboxing works for a method invocation going from java.lang.Integer to int
2495 Expression e = new SimpleMethodInvocation(NONE,
2496 new Word(NONE, "myMethod"),
2497 new ParenthesizedExpressionList(NONE, new Expression[]{
2498 new SimpleNameReference(NONE, new Word(NONE, "i"))}));
2499 BodyItemI[] bii = new BodyItemI[] {new ExpressionStatement(NONE, e)};
2500 BracedBody b = new BracedBody(NONE, bii);
2501
2502 VariableData vd1 = new VariableData("i", _publicMav, symbolTable.get("java.lang.Integer"), true, _sd1);
2503 VariableData vd2 = new VariableData("i", _publicMav, SymbolData.INT_TYPE, true, null);
2504 _sd1.addVar(vd1);
2505 MethodData md =
2506 new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE, new VariableData[] {vd2},
2507 new String[0], null, NULL_LITERAL);
2508 vd2.setEnclosingData(md);
2509 _sd1.addMethod(md);
2510
2511 b.visit(new ClassBodyTypeChecker(_sd1, _btc._file, "", new LinkedList<String>(), new LinkedList<String>(),
2512 _sd1.getVars(), new LinkedList<Pair<SymbolData, JExpression>>()));
2513
2514 assertEquals("There should be no errors", 0, errors.size());
2515
2516 //Test that autoboxing works for a method invocation going from double java.lang.Double
2517 bii = new BodyItemI[] {new ExpressionStatement(NONE, e)};
2518 b = new BracedBody(NONE, bii);
2519
2520
2521 vd1 = new VariableData("i", _publicMav, symbolTable.get("java.lang.Double"), true, null);
2522 vd2 = new VariableData("i", _publicMav, SymbolData.DOUBLE_TYPE, true, _sd1);
2523 _sd1.setVars(new LinkedList<VariableData>());
2524 _sd1.addVar(vd2);
2525 _sd1.setMethods(new LinkedList<MethodData>());
2526 md = new MethodData("myMethod", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE, new VariableData[] {vd1},
2527 new String[0], null, NULL_LITERAL);
2528 vd1.setEnclosingData(md);
2529 _sd1.addMethod(md);
2530
2531 b.visit(new ClassBodyTypeChecker(_sd1, _btc._file, "", new LinkedList<String>(), new LinkedList<String>(),
2532 _sd1.getVars(), new LinkedList<Pair<SymbolData, JExpression>>()));
2533
2534
2535 assertEquals("There should be no errors", 0, errors.size());
2536
2537
2538 //Test that autoboxing can find the correct method def.
2539 _sd1.setVars(new LinkedList<VariableData>());
2540 _sd1.addVar(vd2);
2541 _sd1.addVar(new VariableData("t", _publicMav, SymbolData.BOOLEAN_TYPE, false, _sd1));
2542 _sd1.addMethod(new MethodData("myMethod", _publicMav, new TypeParameter[0],
2543 SymbolData.BOOLEAN_TYPE, new VariableData[] {vd2}, new String[0], null,
2544 NULL_LITERAL));
2545
2546 Expression e2 = new SimpleAssignmentExpression(NONE,
2547 new SimpleNameReference(NONE, new Word(NONE, "t")),
2548 e);
2549
2550 BodyItemI[] bii2 = new BodyItemI[] {new ExpressionStatement(NONE, e2)};
2551 BracedBody b2 = new BracedBody(NONE, bii2);
2552
2553 b2.visit(new ClassBodyTypeChecker(_sd1, _btc._file, "", new LinkedList<String>(), new LinkedList<String>(),
2554 _sd1.getVars(), new LinkedList<Pair<SymbolData, JExpression>>()));
2555
2556 assertEquals("There should be no errors", 0, errors.size());
2557
2558 //Test that autoboxing works for a constructor invocation
2559 _sd1.setIsContinuation(false);
2560 symbolTable.put("i.like.monkey", _sd1);
2561 _sd1.setPackage("i.like");
2562 Expression[] expr1 = new Expression[] {new SimpleNameReference(NONE, new Word(NONE, "i"))};
2563 e = new SimpleNamedClassInstantiation(NONE,
2564 new ClassOrInterfaceType(NONE, "i.like.monkey", new Type[0]),
2565 new ParenthesizedExpressionList(NONE, expr1));
2566
2567 bii = new BodyItemI[] {new ExpressionStatement(NONE, e)};
2568 b = new BracedBody(NONE, bii);
2569
2570 _sd1.setVars(new LinkedList<VariableData>());
2571
2572 vd1 = new VariableData("i", _publicMav, symbolTable.get("java.lang.Integer"), true, _sd1);
2573 vd2 = new VariableData("j", _publicMav, SymbolData.INT_TYPE, true, null);
2574
2575 _sd1.addVar(vd1);
2576 _sd1.setMethods(new LinkedList<MethodData>());
2577 md = new MethodData("monkey", _publicMav, new TypeParameter[0], _sd1, new VariableData[]{vd2}, new String[0],
2578 _sd1, NULL_LITERAL);
2579 vd2.setEnclosingData(md);
2580 _sd1.addMethod(md);
2581
2582 b.visit(new ClassBodyTypeChecker(_sd1, _btc._file, "", new LinkedList<String>(), new LinkedList<String>(),
2583 _sd1.getVars(), new LinkedList<Pair<SymbolData, JExpression>>()));
2584
2585 assertEquals("There should be no errors", 0, errors.size());
2586
2587
2588 //Test that autoboxing works for an assignment from int to Integer
2589 ExpressionStatement se = new ExpressionStatement(NONE,
2590 new PlusAssignmentExpression(NONE,
2591 new SimpleNameReference(NONE, new Word(NONE, "i")),
2592 new SimpleNameReference(NONE, new Word(NONE, "j"))));
2593
2594
2595 _sd1.addVar(vd2);
2596 se.visit(new ClassBodyTypeChecker(_sd1, _btc._file, "", new LinkedList<String>(), new LinkedList<String>(),
2597 _sd1.getVars(), new LinkedList<Pair<SymbolData, JExpression>>()));
2598 // System.err.println("*******The errant error message is: \n" + errors.get(0).getFirst());
2599 assertEquals("There should be no errors", 0, errors.size());
2600
2601 //Test that an error is thrown when there is an ambiguous method.
2602 VariableData vd1Obj = new VariableData("i", _publicMav, symbolTable.get("java.lang.Integer"), true, null);
2603 VariableData vd1Prim = new VariableData("i", _publicMav, SymbolData.INT_TYPE, true, null);
2604 VariableData vd2Obj = new VariableData("j", _publicMav, symbolTable.get("java.lang.Short"), true, null);
2605 VariableData vd2Prim = new VariableData("j", _publicMav, SymbolData.SHORT_TYPE, true, null);
2606 MethodData md1 =
2607 new MethodData("myMethod2", _publicMav, new TypeParameter[0], SymbolData.BOOLEAN_TYPE,
2608 new VariableData[] {vd1Prim, vd2Prim}, new String[0], _sd1, NULL_LITERAL);
2609 MethodData md2 =
2610 new MethodData("myMethod2", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE,
2611 new VariableData[] {vd1Obj, vd2Obj}, new String[0], _sd1, NULL_LITERAL);
2612 vd1Obj.setEnclosingData(md2);
2613 vd1Prim.setEnclosingData(md1);
2614 vd2Obj.setEnclosingData(md2);
2615 vd2Prim.setEnclosingData(md1);
2616
2617 _sd1.setMethods(new LinkedList<MethodData>());
2618 _sd1.addMethod(md1);
2619 _sd1.addMethod(md2);
2620 _sd1.setVars(new LinkedList<VariableData>());
2621 _sd1.addVar(vd1Prim);
2622 _sd1.addVar(vd2Obj);
2623 Expression[] expr2 =
2624 new Expression[]{new SimpleNameReference(NONE, new Word(NONE, "i")),
2625 new SimpleNameReference(NONE, new Word(NONE, "j"))};
2626 e = new SimpleMethodInvocation(NONE,
2627 new Word(NONE, "myMethod2"),
2628 new ParenthesizedExpressionList(NONE, expr2));
2629 bii = new BodyItemI[] {new ExpressionStatement(NONE, e)};
2630 b = new BracedBody(NONE, bii);
2631
2632 b.visit(new ClassBodyTypeChecker(_sd1, _btc._file, "", new LinkedList<String>(), new LinkedList<String>(),
2633 _sd1.getVars(), new LinkedList<Pair<SymbolData, JExpression>>()));
2634
2635 assertEquals("There should be one error", 1, errors.size());
2636 assertEquals("The error message should be correct",
2637 "myMethod2(int, java.lang.Short) is an ambiguous invocation. " +
2638 "It matches both myMethod2(int, short) and myMethod2(java.lang.Integer, java.lang.Short)",
2639 errors.get(0).getFirst());
2640
2641 // test that if a method is overridden that it still works
2642 SymbolData subSd = new SymbolData("sub");
2643 subSd.setSuperClass(_sd1);
2644 subSd.setIsContinuation(false);
2645
2646 _sd1.setMethods(new LinkedList<MethodData>());
2647 _sd1.addMethod(md1);
2648 MethodData md3 = new MethodData("myMethod2", _publicMav, new TypeParameter[0], SymbolData.BOOLEAN_TYPE,
2649 new VariableData[] {vd1Prim, vd2Prim}, new String[0], _sd1,
2650 NULL_LITERAL);
2651 b.visit(new ClassBodyTypeChecker(subSd, _btc._file, "", new LinkedList<String>(), new LinkedList<String>(),
2652 _sd1.getVars(), new LinkedList<Pair<SymbolData, JExpression>>()));
2653 assertEquals("There should still be one error", 1, errors.size()); // Generated a duplicate error
2654
2655 // test that if a sd1 has something that's ambiguous, so the superclass is ambiguous, the error is only thrown
2656 // once when calling the method in the subclass.
2657 subSd.setMethods(new LinkedList<MethodData>());
2658 _sd1.addMethod(md2);
2659 b.visit(new ClassBodyTypeChecker(subSd, _btc._file, "", new LinkedList<String>(), new LinkedList<String>(),
2660 _sd1.getVars(), new LinkedList<Pair<SymbolData, JExpression>>()));
2661 assertEquals("There should still be one error", 1, errors.size());
2662 assertEquals("The error message should be correct",
2663 "myMethod2(int, java.lang.Short) is an ambiguous invocation. It matches both " +
2664 "myMethod2(int, short) and myMethod2(java.lang.Integer, java.lang.Short)",
2665 errors.get(0).getFirst());
2666 }
2667
2668 public void testForInnerClassDef() {
2669 //TODO: implement this test (for advanced level)
2670 }
2671 }
2672 }