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    /**Do the TypeChecking appropriate to the context of a class body.  Common to all Language Levels.*/
049    public class InterfaceBodyTypeChecker extends SpecialTypeChecker {
050      
051      /**The SymbolData corresponding to this interface.*/
052      private SymbolData _symbolData;
053      
054      /* Constructor for InterfaceBodyTypeChecker.  Adds all the variables in the symbol data to the list of what can be 
055       * seen from this context, since interface fields can always be seen.
056       * @param sd  The SymbolData of the interface we are type checking.
057       * @param file  The File corresponding to the source file we are checking.
058       * @param packageName  The package of the source file.
059       * @param importedFiles  A list of the names of the classes that are specifically imported in the source file
060       * @param importedPackages  A list of the names of the packages that are imported in the source file.
061       * @param vars  A list of the variable datas that can be seen and have been given a value before this context
062       * @param thrown  The exceptions that are thrown
063       */
064      public InterfaceBodyTypeChecker(SymbolData sd, File file, String packageName, LinkedList<String> importedFiles, LinkedList<String> importedPackages, LinkedList<VariableData> vars, LinkedList<Pair<SymbolData, JExpression>> thrown) {
065        super(sd, file, packageName, importedFiles, importedPackages, vars, thrown);
066        _vars.addAll(sd.getVars());
067        _symbolData = sd;
068      }
069    
070      /**@return the SymbolData corresponding to the enclosing interface*/
071      protected Data _getData() {
072        return _symbolData;
073      }
074    
075      
076      /** All fields in interfaces must be initialized where they are declared, so throw an error.*/
077      public TypeData forUninitializedVariableDeclarator(UninitializedVariableDeclarator that) {
078        _addError("All fields in interfaces must be initialized", that);
079        return null;
080      }
081    
082      /**
083       * Resolve all the stuff that is stored in the AbstractMethodDef.  Find the method data
084       * that was created on the first pass to correspond to this method, and make sure that this 
085       * method doesn't resolve another method with a different return type.
086       */
087      public TypeData forAbstractMethodDef(AbstractMethodDef that) {
088        final TypeData mavRes = that.getMav().visit(this);
089        final TypeData[] typeParamsRes = makeArrayOfRetType(that.getTypeParams().length);
090        for (int i = 0; i < that.getTypeParams().length; i++) {
091          typeParamsRes[i] = that.getTypeParams()[i].visit(this);
092        }
093        final TypeData resRes = getSymbolData(that.getResult().getName(), _symbolData, that);//that.getResult().visit(this);
094        final TypeData nameRes = that.getName().visit(this);
095        final TypeData[] paramsRes = makeArrayOfRetType(that.getParams().length);
096        for (int i = 0; i<paramsRes.length; i++) {
097          paramsRes[i] = getSymbolData(that.getParams()[i].getDeclarator().getType().getName(), _symbolData, that.getParams()[i]);
098        }
099        final TypeData[] throwsRes = makeArrayOfRetType(that.getThrows().length);
100        for (int i = 0; i < that.getThrows().length; i++) {
101          throwsRes[i] = getSymbolData(that.getThrows()[i].getName(), _symbolData, that.getThrows()[i]);//that.getThrows()[i].visit(this);
102        }
103        // Ensure that this method doesn't override another method with a different return type.
104        MethodData md = _symbolData.getMethod(that.getName().getText(), paramsRes);
105        if (md == null) {
106          throw new RuntimeException("Internal Program Error: Could not find the method " + that.getName().getText() + " in interface " + _symbolData.getName() + ".  Please report this bug.");
107        }
108        SymbolData.checkDifferentReturnTypes(md, _symbolData, LanguageLevelConverter.OPT.javaVersion());
109        return resRes;
110      }
111      
112    
113      /**This error should be thrown in the first pass, but throw it again here, just in case*/
114       public TypeData forConcreteMethodDef(ConcreteMethodDef that) {
115         _addError("Concrete method definitions cannot appear in interfaces", that);
116         return null;
117       }
118      
119       /**
120        * Try to resolve the type that is referenced.  Check to see if it is accessible from this context.
121        */
122        public TypeData forTypeOnly(Type that) {
123        Data sd = getSymbolData(that.getName(), _symbolData, that);
124        if (sd != null) {sd = sd.getOuterData();}
125        while (sd != null && !LanguageLevelVisitor.isJavaLibraryClass(sd.getSymbolData().getName())) {
126          if (!checkAccess(that, sd.getMav(), sd.getName(), sd.getSymbolData(), _symbolData, "class or interface")) {
127            return null;
128          }
129          sd = sd.getOuterData();
130        }
131        return null;
132      }
133        
134      
135       /**
136        * Test the methods declared in the above class.
137        */
138      public static class InterfaceBodyTypeCheckerTest extends TestCase {
139        
140        private InterfaceBodyTypeChecker _ibbtc;
141        
142        private SymbolData _sd1;
143        private SymbolData _sd2;
144        private SymbolData _sd3;
145        private SymbolData _sd4;
146        private SymbolData _sd5;
147        private SymbolData _sd6;
148        private ModifiersAndVisibility _publicMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"});
149        private ModifiersAndVisibility _protectedMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"protected"});
150        private ModifiersAndVisibility _privateMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"private"});
151        private ModifiersAndVisibility _packageMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[0]);
152        private ModifiersAndVisibility _abstractMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"abstract"});
153        private ModifiersAndVisibility _finalMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"final"});
154        
155        
156        public InterfaceBodyTypeCheckerTest() {
157          this("");
158        }
159        public InterfaceBodyTypeCheckerTest(String name) {
160          super(name);
161    //      _file = File.createTempFile("DrJava-test", ".java");
162    //      _bv = new BeginnerVisitor(new File(""), "", new LinkedList<String>(), new LinkedList<String>(), 
163    //                                 new LinkedList<String>());
164        }
165        
166        public void setUp() {
167          _sd1 = new SymbolData("i.like.monkey");
168          _sd2 = new SymbolData("i.like.giraffe");
169          _sd3 = new SymbolData("zebra");
170          _sd4 = new SymbolData("u.like.emu");
171          _sd5 = new SymbolData("");
172          _sd6 = new SymbolData("cebu");
173          errors = new LinkedList<Pair<String, JExpressionIF>>();
174          LanguageLevelConverter.symbolTable.clear();
175          LanguageLevelConverter._newSDs.clear();
176          _ibbtc = 
177            new InterfaceBodyTypeChecker(_sd1, new File(""), "", new LinkedList<String>(), new LinkedList<String>(),
178                                         new LinkedList<VariableData>(), new LinkedList<Pair<SymbolData, JExpression>>());
179          LanguageLevelConverter.OPT = new Options(JavaVersion.JAVA_5, EmptyIterable.<File>make());
180          _ibbtc._importedPackages.addFirst("java.lang");
181        }
182        
183        public void testForUninitializedVariableDeclaratorOnly() {
184          VariableData vd1 = new VariableData("Mojo", _publicMav, SymbolData.INT_TYPE, false, _sd1);
185          _sd1.addVar(vd1);
186          UninitializedVariableDeclarator uvd = 
187            new UninitializedVariableDeclarator(SourceInfo.NONE, 
188                                                new PrimitiveType(SourceInfo.NONE, "int"), 
189                                                new Word(SourceInfo.NONE, "Mojo"));
190          uvd.visit(_ibbtc);
191          _ibbtc.forUninitializedVariableDeclaratorOnly(uvd, SymbolData.INT_TYPE, null);
192          assertEquals("There should be one error", 1, errors.size());
193          assertEquals("The error message should be correct", "All fields in interfaces must be initialized", 
194                       errors.get(0).getFirst());
195        }
196        
197        public void testForInitializedVariableDeclaratorOnly() {
198          VariableData vd1 = new VariableData("Mojo", _publicMav, SymbolData.INT_TYPE, false, _sd1);
199          _sd1.addVar(vd1);
200          InitializedVariableDeclarator ivd = 
201            new InitializedVariableDeclarator(SourceInfo.NONE, 
202                                              new PrimitiveType(SourceInfo.NONE, "int"), 
203                                              new Word(SourceInfo.NONE, "Mojo"), 
204                                              new IntegerLiteral(SourceInfo.NONE, 1));
205          ivd.visit(_ibbtc);
206          assertEquals("There should be no errors.", 0, errors.size());
207          assertTrue("_vars should contain Mojo.", _ibbtc._vars.contains(vd1));
208          ivd = new InitializedVariableDeclarator(SourceInfo.NONE, 
209                                                  new PrimitiveType(SourceInfo.NONE, "int"), 
210                                                  new Word(SourceInfo.NONE, "Santa's Little Helper"), 
211                                                  new IntegerLiteral(SourceInfo.NONE, 1));
212          try {
213            ivd.visit(_ibbtc);
214            fail("Should have thrown a RuntimeException because there's no field named Santa's Little Helper.");
215          }
216          catch (RuntimeException re) {
217            assertEquals("The error message should be correct.", 
218                         "Internal Program Error: The field or variable Santa's Little Helper was not found in this block.  Please report this bug.", re.getMessage());
219          }
220        }
221        
222        public void testForConcreteMethodDef() {
223          FormalParameter[] fps = new FormalParameter[] {
224            new FormalParameter(SourceInfo.NONE, 
225                                new UninitializedVariableDeclarator(SourceInfo.NONE, 
226                                                                    new PrimitiveType(SourceInfo.NONE, "double"), 
227                                                                    new Word (SourceInfo.NONE, "field1")),
228                                false),
229            new FormalParameter(SourceInfo.NONE, 
230                                new UninitializedVariableDeclarator(SourceInfo.NONE, 
231                                                                    new PrimitiveType(SourceInfo.NONE, "boolean"), 
232                                                                    new Word (SourceInfo.NONE, "field2")),
233                                false)};
234          
235          ConcreteMethodDef cmd = 
236            new ConcreteMethodDef(SourceInfo.NONE, 
237                                  _packageMav, 
238                                  new TypeParameter[0], 
239                                  new PrimitiveType(SourceInfo.NONE, "int"), 
240                                  new Word(SourceInfo.NONE, "methodName"),
241                                  fps,
242                                  new ReferenceType[0], 
243                                  new BracedBody(SourceInfo.NONE, new BodyItemI[] {
244            new ValueReturnStatement(SourceInfo.NONE, 
245                                     new IntegerLiteral(SourceInfo.NONE, 5))}));
246          
247          MethodData md = new MethodData("methodName", 
248                                         _packageMav, 
249                                         new TypeParameter[0], 
250                                         SymbolData.INT_TYPE, 
251                                         new VariableData[] { new VariableData(SymbolData.DOUBLE_TYPE), new VariableData(SymbolData.BOOLEAN_TYPE) },
252                                         new String[0],
253                                         _sd1,
254                                         null); // no SourceInfo
255                                         
256          md.getParams()[0].setEnclosingData(md);                               
257          md.getParams()[1].setEnclosingData(md);                               
258    
259          _sd1.addMethod(md);
260    //      _ibbtc._vars.addLast(new VariableData("field1", _packageMav, SymbolData.DOUBLE_TYPE));
261          cmd.visit(_ibbtc);
262          assertEquals("There should be one error.", 1, errors.size());
263          assertEquals("The error message should be correct", "Concrete method definitions cannot appear in interfaces", errors.get(0).getFirst());
264        }
265        
266        public void testForTypeOnly() {
267          Type t = new PrimitiveType(SourceInfo.NONE, "double");
268          t.visit(_ibbtc);
269          assertEquals("There should be no errors", 0, errors.size());
270          
271          SymbolData sd = new SymbolData("Adam");
272          sd.setIsContinuation(false);
273          symbolTable.put("Adam", sd);
274          sd.setMav(_publicMav);
275          t = new ClassOrInterfaceType(SourceInfo.NONE, "Adam", new Type[0]);
276          t.visit(_ibbtc);
277          assertEquals("There should still be no errors", 0, errors.size());
278          
279          SymbolData innerSd = new SymbolData("Adam$Wulf");
280          innerSd.setIsContinuation(false);
281          sd.addInnerClass(innerSd);
282          innerSd.setOuterData(sd);
283          innerSd.setMav(_publicMav);
284          _ibbtc.symbolTable.put("USaigehgihdsgslghdlighs", innerSd);
285          t = new ClassOrInterfaceType(SourceInfo.NONE, "Adam.Wulf", new Type[0]);
286          t.visit(_ibbtc);
287          assertEquals("There should still be no errors", 0, errors.size());
288          
289          innerSd.setMav(_privateMav);
290          t = new ClassOrInterfaceType(SourceInfo.NONE, "Adam.Wulf", new Type[0]);
291          t.visit(_ibbtc);
292          String tcSDName =  _ibbtc._symbolData.getName();
293          assertEquals("There should be one error", 1, errors.size());
294          assertEquals("The error message should be correct", 
295                       "The class or interface Adam.Wulf in Adam.Wulf is private and cannot be accessed from " + tcSDName,
296                       errors.get(0).getFirst());
297          
298          sd.setMav(_privateMav);
299          innerSd.setMav(_publicMav);
300          t = new ClassOrInterfaceType(SourceInfo.NONE, "Adam.Wulf", new Type[0]);
301          t.visit(_ibbtc);
302          assertEquals("There should be two errors", 2, errors.size());
303          assertEquals("The error message should be correct", 
304                       "The class or interface Adam in Adam is private and cannot be accessed from " + tcSDName,
305                       errors.get(1).getFirst());
306        }
307      }
308    }