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 java.util.*;
042    import java.io.*;
043    import edu.rice.cs.plt.reflect.JavaVersion;
044    import edu.rice.cs.plt.iter.*;
045    
046    import junit.framework.TestCase;
047    
048    /**Does TypeChecking for the context of a Try-Catch body.  Common to all LanguageLevels.*/
049    public class TryCatchBodyTypeChecker extends BodyTypeChecker {
050    
051    
052      /* Constructor for TryCatchBodyTypeChecker.  Delegates the initialization to the BodyTypeChecker
053       * @param bodyData  The enclosing BodyData for the context we are type checking.
054       * @param file  The File corresponding to the source file.
055       * @param packageName  The package name from the source file.
056       * @param importedFiles  The names of the files that are specifically imported (through a class import statement) in the source file.
057       * @param importedPackages  The names of all the packages that are imported through a package import statement in the source file.
058       * @param vars  The list of VariableData that have already been defined (used so we can make sure we don't use a variable before it has been defined).
059       * @param thrown  The list of exceptions thrown in this body
060       */
061      public TryCatchBodyTypeChecker(BodyData bodyData, File file, String packageName, LinkedList<String> importedFiles, LinkedList<String> importedPackages, LinkedList<VariableData> vars, LinkedList<Pair<SymbolData, JExpression>> thrown) {
062        super(bodyData, file, packageName, importedFiles, importedPackages, vars, thrown);
063      }
064     
065      
066      /** Create a new instance of this class for visiting inner bodies. */
067      protected BodyTypeChecker createANewInstanceOfMe(BodyData bodyData, File file, String pakage, LinkedList<String> importedFiles, LinkedList<String> importedPackages, LinkedList<VariableData> vars, LinkedList<Pair<SymbolData, JExpression>> thrown) {
068        return new TryCatchBodyTypeChecker(bodyData, file, pakage, importedFiles, importedPackages, vars, thrown);
069      }
070      
071      /** Overwritten here, becuase it is okay for there to be thrown exceptions in the middle of a try catch. */
072      public TypeData forBracedBody(BracedBody that) {
073        final TypeData[] items_result = makeArrayOfRetType(that.getStatements().length);
074        for (int i = 0; i < that.getStatements().length; i++) {
075          items_result[i] = that.getStatements()[i].visit(this);
076        }
077        return forBracedBodyOnly(that, items_result);
078      }
079      
080      /** Make sure that every Exception in thrown is either in caught or in the list of what can be thrown from where we are.
081        * Also make sure that every Exception that is declared to be thrown or caught is actually thrown.
082        * Overrides the same method in BodyTypeChecker.
083        * @param that  The TryCatchStatement we are currently working with
084        * @param caught_array  The SymbolData[] of exceptions that are explicitely caught.
085        * @param thrown  The LinkedList of SymbolData of exceptions that are thrown.  This will be modified.
086        */
087      protected void compareThrownAndCaught(TryCatchStatement that, SymbolData[] caught_array, 
088                                            LinkedList<Pair<SymbolData, JExpression>> thrown) {
089        LinkedList<Pair<SymbolData, JExpression>> copyOfThrown = new LinkedList<Pair<SymbolData, JExpression>>();
090        for (Pair<SymbolData, JExpression> p : thrown) {
091          copyOfThrown.addLast(p);
092        }
093        //Make sure that every Exception in thrown is either caught or in the list of what can be thrown
094        for (Pair<SymbolData, JExpression> p : copyOfThrown) {
095          SymbolData sd = p.getFirst();
096          // Iterate over the caught array and see if the current thrown exception is a subclass of one of the exceptions.
097          for (SymbolData currCaughtSD : caught_array) {
098            if (sd.isSubClassOf(currCaughtSD) || (!isUncaughtCheckedException(sd, new NullLiteral(SourceInfo.NONE)))) {
099              thrown.remove(p);
100            }
101          }
102        }
103        makeSureCaughtStuffWasThrown(that, caught_array, copyOfThrown);
104      }
105      
106       /**
107        * Test the methods declared in the above class.
108        */
109      public static class TryCatchBodyTypeCheckerTest extends TestCase {
110        
111        private TryCatchBodyTypeChecker _tcbtc;
112        
113        private BodyData _bd1;
114        private BodyData _bd2;
115        
116        private SymbolData _sd1;
117        private SymbolData _sd2;
118        private SymbolData _sd3;
119        private SymbolData _sd4;
120        private SymbolData _sd5;
121        private SymbolData _sd6;
122        private ModifiersAndVisibility _publicMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"});
123        private ModifiersAndVisibility _protectedMav = 
124          new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"protected"});
125        private ModifiersAndVisibility _privateMav = 
126          new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"private"});
127        private ModifiersAndVisibility _packageMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[0]);
128        private ModifiersAndVisibility _abstractMav = 
129          new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"abstract"});
130        private ModifiersAndVisibility _finalMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"final"});
131        
132        
133        public TryCatchBodyTypeCheckerTest() { this(""); }
134        public TryCatchBodyTypeCheckerTest(String name) { super(name); }
135        
136        public void setUp() {
137          _sd1 = new SymbolData("i.like.monkey");
138          _sd2 = new SymbolData("i.like.giraffe");
139          _sd3 = new SymbolData("zebra");
140          _sd4 = new SymbolData("u.like.emu");
141          _sd5 = new SymbolData("");
142          _sd6 = new SymbolData("cebu");
143    
144          _bd1 = new MethodData("monkey", 
145                                _packageMav, 
146                                new TypeParameter[0], 
147                                _sd1, 
148                                new VariableData[] { new VariableData("i", _publicMav, SymbolData.INT_TYPE, true, null), new VariableData(SymbolData.BOOLEAN_TYPE) },
149                                new String[0],
150                                _sd1,
151                                null); // no SourceInfo
152          ((MethodData) _bd1).getParams()[0].setEnclosingData(_bd1);
153          ((MethodData) _bd1).getParams()[1].setEnclosingData(_bd1);
154                                
155          errors = new LinkedList<Pair<String, JExpressionIF>>();
156          LanguageLevelConverter.symbolTable.clear();
157          _bd1.addEnclosingData(_sd1);
158          _bd1.addFinalVars(((MethodData)_bd1).getParams());
159          _tcbtc = new TryCatchBodyTypeChecker(_bd1, new File(""), "", new LinkedList<String>(), new LinkedList<String>(), new LinkedList<VariableData>(), new LinkedList<Pair<SymbolData, JExpression>>());
160          LanguageLevelConverter.OPT = new Options(JavaVersion.JAVA_5, EmptyIterable.<File>make());
161          _tcbtc._importedPackages.addFirst("java.lang");
162        }
163        
164        
165        public void testCreateANewInstanceOfMe() {
166          //make sure that the correct visitor is returned from createANewInstanceOfMe
167          BodyTypeChecker btc = _tcbtc.createANewInstanceOfMe(_tcbtc._bodyData, _tcbtc._file, _tcbtc._package, _tcbtc._importedFiles, _tcbtc._importedPackages, _tcbtc._vars, _tcbtc._thrown);
168          assertTrue("Should be an instance of ConstructorBodyTypeChecker", btc instanceof TryCatchBodyTypeChecker);
169        }
170        
171        public void testForBracedBody() {
172          //make sure it is okay to have a uncaught exception in a braced body
173          BracedBody bb = new BracedBody(SourceInfo.NONE, 
174                                         new BodyItemI[] { 
175            new ThrowStatement(SourceInfo.NONE, 
176                               new SimpleNamedClassInstantiation(SourceInfo.NONE, 
177                                             new ClassOrInterfaceType(SourceInfo.NONE, 
178                                                                     "java.util.prefs.BackingStoreException", 
179                                                                     new Type[0]), 
180                                                                 new ParenthesizedExpressionList(SourceInfo.NONE, new Expression[] {new StringLiteral(SourceInfo.NONE, "arg")})))});
181          
182          LanguageLevelVisitor llv = 
183            new LanguageLevelVisitor(new File(""), 
184                                     "",
185                                     null, // enclosingClassName for top level traversal
186                                     new LinkedList<String>(), 
187                                     new LinkedList<String>(), 
188                                     new HashSet<String>(), 
189                                     new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
190                                     new LinkedList<Command>());
191          llv.errors = new LinkedList<Pair<String, JExpressionIF>>();
192          llv._errorAdded=false;
193          LanguageLevelConverter.symbolTable.clear();
194          LanguageLevelConverter._newSDs.clear();
195          LanguageLevelConverter.loadSymbolTable();
196          llv.continuations = new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>();
197          llv.visitedFiles = new LinkedList<Pair<LanguageLevelVisitor, edu.rice.cs.javalanglevels.tree.SourceFile>>();      
198    //      llv._hierarchy = new Hashtable<String, TypeDefBase>();
199          llv._classesInThisFile = new HashSet<String>();
200          
201          SymbolData o = symbolTable.get("java.lang.Object");
202    //      o.setIsContinuation(false);
203    //      o.setMav(_publicMav);
204    //      symbolTable.put("java.lang.Object", o);
205                 
206          SymbolData string = new SymbolData("java.lang.String");
207          string.setIsContinuation(false);
208          string.setMav(_publicMav);
209          string.setSuperClass(o);   // a white lie for this test   
210          symbolTable.put("java.lang.String", string);
211    
212          SymbolData e = llv.getSymbolData("java.util.prefs.BackingStoreException", SourceInfo.NONE, true);
213          
214          bb.visit(_tcbtc);
215          assertEquals("There should be no errors because it's ok to have uncaught exceptions in this visitor", 
216                       0, 
217                       errors.size());
218        }
219        
220        public void testCompareThrownAndCaught() {
221          BracedBody emptyBody = new BracedBody(SourceInfo.NONE, new BodyItemI[0]);
222          Block b = new Block(SourceInfo.NONE, emptyBody);
223    
224          PrimitiveType intt = new PrimitiveType(SourceInfo.NONE, "int");
225          UninitializedVariableDeclarator uvd = 
226            new UninitializedVariableDeclarator(SourceInfo.NONE, intt, new Word(SourceInfo.NONE, "i"));
227          FormalParameter param = 
228            new FormalParameter(SourceInfo.NONE, 
229                                new UninitializedVariableDeclarator(SourceInfo.NONE, intt, new Word(SourceInfo.NONE, "j")), 
230                                false);
231    
232          NormalTryCatchStatement ntcs = new NormalTryCatchStatement(SourceInfo.NONE, b, new CatchBlock[] {new CatchBlock(SourceInfo.NONE,  param, b)});
233    
234          SymbolData javaLangThrowable = _tcbtc.getSymbolData("java.lang.Throwable", ntcs, false, true); 
235          _tcbtc.symbolTable.put("java.lang.Throwable", javaLangThrowable);
236          SymbolData exception = new SymbolData("my.crazy.exception");
237          exception.setSuperClass(javaLangThrowable);
238          SymbolData exception2 = new SymbolData("A&M.beat.Rice.in.BaseballException");
239          exception2.setSuperClass(javaLangThrowable);
240          SymbolData exception3 = new SymbolData("aegilha");
241          exception3.setSuperClass(exception2);
242          SymbolData[] caught_array = new SymbolData[] { exception, exception2 };
243          LinkedList<Pair<SymbolData, JExpression>> thrown = new LinkedList<Pair<SymbolData, JExpression>>();
244          thrown.addLast(new Pair<SymbolData, JExpression>(exception, ntcs));
245          thrown.addLast(new Pair<SymbolData, JExpression>(exception2, ntcs));
246          thrown.addLast(new Pair<SymbolData, JExpression>(exception3, ntcs));
247          
248          _tcbtc.compareThrownAndCaught(ntcs, caught_array, thrown);
249    //      System.err.println("thrown = " + thrown);
250          assertTrue("Thrown should have no elements", thrown.isEmpty());
251    
252          
253          _tcbtc.compareThrownAndCaught(ntcs, new SymbolData[] {exception2}, thrown);
254          assertEquals("There should be one error", 1, errors.size());
255          assertEquals("The error message should be correct", "The exception A&M.beat.Rice.in.BaseballException is never thrown in the body of the corresponding try block", errors.get(0).getFirst());
256    
257        }
258      }
259    }