001    /* Copyright (c) 2005-2008, Torbjorn Ekman
002     * All rights reserved.
003     *
004     * Redistribution and use in source and binary forms, with or without
005     * modification, are permitted provided that the following conditions are met:
006     *
007     * 1. Redistributions of source code must retain the above copyright notice,
008     * this list of conditions and the following disclaimer.
009     *
010     * 2. Redistributions in binary form must reproduce the above copyright notice,
011     * this list of conditions and the following disclaimer in the documentation
012     * and/or other materials provided with the distribution.
013     *
014     * 3. Neither the name of the copyright holder nor the names of its
015     * contributors may be used to endorse or promote products derived from this
016     * software without specific prior written permission.
017     *
018     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
022     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
028     * POSSIBILITY OF SUCH DAMAGE.
029     */
030    
031    import java.util.*;
032    
033    aspect VariableScope {
034      // lookupVariable(String name) shows the variables in scope named name
035      inh lazy SimpleSet TypeDecl.lookupVariable(String name);
036      inh lazy SimpleSet BodyDecl.lookupVariable(String name);
037      inh SimpleSet Stmt.lookupVariable(String name);
038      inh lazy SimpleSet Block.lookupVariable(String name);
039      inh lazy SimpleSet ForStmt.lookupVariable(String name);
040      inh SimpleSet Expr.lookupVariable(String name);
041      inh lazy SimpleSet CatchClause.lookupVariable(String name);
042      inh SimpleSet VariableDeclaration.lookupVariable(String name);
043      inh SimpleSet ParameterDeclaration.lookupVariable(String name);
044    
045      eq Program.getChild().lookupVariable(String name) = SimpleSet.emptySet;
046    
047      // 6.5.6.1
048      eq TypeDecl.getBodyDecl(int i).lookupVariable(String name) {
049        SimpleSet list = memberFields(name);
050        if (!list.isEmpty()) {
051          return list;
052        }
053        list = lookupVariable(name);
054        if (inStaticContext() || isStatic()) {
055          list = removeInstanceVariables(list);
056        }
057        return list;
058      }
059    
060      // The scope of a parameter of a method is the entire body of the method
061      eq MethodDecl.getBlock().lookupVariable(String name) {
062        SimpleSet set = parameterDeclaration(name);
063        // A declaration of a method parameter name shadows any other variable declarations
064        if (!set.isEmpty()) {
065          return set;
066        }
067        // Delegate to other declarations in scope
068        return lookupVariable(name);
069      }
070      // A method declaration may only declare one parameter named name
071      // This is enforced by a check that the declaration in scope for a declaration is itself
072      eq MethodDecl.getParameter().lookupVariable(String name) = parameterDeclaration(name);
073    
074      eq ConstructorDecl.getBlock().lookupVariable(String name) {
075        SimpleSet set = parameterDeclaration(name);
076        if (!set.isEmpty()) {
077          return set;
078        }
079        return lookupVariable(name);
080      }
081    
082      eq ConstructorDecl.getParsedConstructorInvocation().lookupVariable(String name) {
083        SimpleSet set = parameterDeclaration(name);
084        if (!set.isEmpty()) {
085          return set;
086        }
087        for (Iterator iter = lookupVariable(name).iterator(); iter.hasNext(); ) {
088          Variable v = (Variable) iter.next();
089          if (!hostType().memberFields(name).contains(v) || v.isStatic()) {
090            set = set.add(v);
091          }
092        }
093        return set;
094      }
095    
096      eq ConstructorDecl.getImplicitConstructorInvocation().lookupVariable(String name) {
097        SimpleSet set = parameterDeclaration(name);
098        if (!set.isEmpty()) {
099          return set;
100        }
101        for (Iterator iter = lookupVariable(name).iterator(); iter.hasNext(); ) {
102          Variable v = (Variable) iter.next();
103          if (!hostType().memberFields(name).contains(v) || v.isStatic()) {
104            set = set.add(v);
105          }
106        }
107        return set;
108      }
109    
110      eq ConstructorDecl.getParameter().lookupVariable(String name) = parameterDeclaration(name);
111    
112      eq VarDeclStmt.getSingleDecl(int index).lookupVariable(String name) {
113        for (int i = index; i >= 0; --i) {
114          if (getSingleDecl(i).declaresVariable(name)) {
115            return getSingleDecl(i);
116          }
117        }
118        return lookupVariable(name);
119      }
120    
121      /**
122       * The scope of a local variable declaration in a block is the rest of
123       * the block in which the declaration appears
124       */
125      eq Block.getStmt(int index).lookupVariable(String name) {
126        VariableDeclaration v = localVariableDeclaration(name);
127        // declare before use and shadowing
128        if (v != null && declaredBeforeUse(v, index)) {
129          return v;
130        }
131        return lookupVariable(name);
132      }
133    
134      // The scope of the parameter of an exception handler that is declared in a
135      // catch clause of a try statement is the entire block associated with the catch
136      eq CatchClause.getBlock().lookupVariable(String name) {
137        SimpleSet set = parameterDeclaration(name);
138        if (!set.isEmpty()) {
139          return set;
140        }
141        return lookupVariable(name);
142      }
143      eq BasicCatch.getParameter().lookupVariable(String name) = parameterDeclaration(name);
144    
145      // The scope of a local variable declared in the ForInit part of the for
146      // statement includes all of the following:
147      eq ForStmt.getInitStmt().lookupVariable(String name) = localLookup(name);
148      eq ForStmt.getCondition().lookupVariable(String name) = localLookup(name);
149      eq ForStmt.getUpdateStmt().lookupVariable(String name) = localLookup(name);
150      eq ForStmt.getStmt().lookupVariable(String name) = localLookup(name);
151      syn lazy SimpleSet ForStmt.localLookup(String name) {
152        VariableDeclaration v = localVariableDeclaration(name);
153        if (v != null) {
154          return v;
155        }
156        return lookupVariable(name);
157      }
158    
159      // Return the first variable declaration named name
160    
161      syn lazy SimpleSet MethodDecl.parameterDeclaration(String name) {
162        for (int i = 0; i < getNumParameter(); i++) {
163          if (getParameter(i).name().equals(name)) {
164            return (ParameterDeclaration) getParameter(i);
165          }
166        }
167        return SimpleSet.emptySet;
168      }
169    
170      syn lazy SimpleSet ConstructorDecl.parameterDeclaration(String name) {
171        for (int i = 0; i < getNumParameter(); i++) {
172          if (getParameter(i).name().equals(name)) {
173            return (ParameterDeclaration) getParameter(i);
174          }
175        }
176        return SimpleSet.emptySet;
177      }
178    
179      syn lazy SimpleSet CatchClause.parameterDeclaration(String name) = SimpleSet.emptySet;
180      eq BasicCatch.parameterDeclaration(String name) =
181          getParameter().name().equals(name)
182          ? getParameter()
183          : SimpleSet.emptySet;
184    
185      syn lazy VariableDeclaration Block.localVariableDeclaration(String name) {
186        for (Stmt stmt: getStmtList()) {
187          VariableDeclaration decl = stmt.variableDeclaration(name);
188          if (decl != null) {
189            return decl;
190          }
191        }
192        return null;
193      }
194    
195      syn lazy VariableDeclaration ForStmt.localVariableDeclaration(String name) {
196        for (Stmt stmt: getInitStmtList()) {
197          VariableDeclaration decl = stmt.variableDeclaration(name);
198          if (decl != null) {
199            return decl;
200          }
201        }
202        return null;
203      }
204    
205      syn VariableDeclaration Stmt.variableDeclaration(String name) = null;
206    
207      eq VarDeclStmt.variableDeclaration(String name) {
208        for (VariableDeclaration decl: getSingleDeclList()) {
209          if (decl.declaresVariable(name)) {
210            return decl;
211          }
212        }
213        return null;
214      }
215    
216      syn boolean VariableDeclaration.declaresVariable(String name) = getID().equals(name);
217    
218      eq MethodAccess.getArg().lookupVariable(String name) = unqualifiedScope().lookupVariable(name);
219      eq ConstructorAccess.getArg().lookupVariable(String name) =
220          unqualifiedScope().lookupVariable(name);
221      eq SuperConstructorAccess.getArg().lookupVariable(String name) =
222          unqualifiedScope().lookupVariable(name);
223      eq ArrayAccess.getExpr().lookupVariable(String name) =
224          unqualifiedScope().lookupVariable(name);
225      eq ArrayTypeWithSizeAccess.getExpr().lookupVariable(String name) =
226          unqualifiedScope().lookupVariable(name);
227      eq ClassInstanceExpr.getArg().lookupVariable(String name) =
228          unqualifiedScope().lookupVariable(name);
229    
230      eq AbstractDot.getRight().lookupVariable(String name) = getLeft().qualifiedLookupVariable(name);
231    
232      eq ParseName.qualifiedLookupVariable(String name) = SimpleSet.emptySet;
233    
234      // Access control specifies the part of a program where a declared entity can
235      // be referred to by a qualified name, field access expression, method
236      // invocation expression without a simple name
237      syn SimpleSet Expr.qualifiedLookupVariable(String name) {
238        if (type().accessibleFrom(hostType())) {
239          return keepAccessibleFields(type().memberFields(name));
240        }
241        return SimpleSet.emptySet;
242      }
243      eq PackageAccess.qualifiedLookupVariable(String name) = SimpleSet.emptySet;
244      eq TypeAccess.qualifiedLookupVariable(String name) {
245        if (type().accessibleFrom(hostType())) {
246          SimpleSet c = type().memberFields(name);
247          c = keepAccessibleFields(c);
248          if (type().isClassDecl() && c.size() == 1) {
249            c = removeInstanceVariables(c);
250          }
251          return c;
252        }
253        return SimpleSet.emptySet;
254      }
255    
256      /**
257       * Remove fields that are not accessible when using this Expr as qualifier
258       * @return a set containing the accessible fields
259       */
260      public SimpleSet Expr.keepAccessibleFields(SimpleSet oldSet) {
261        SimpleSet newSet = SimpleSet.emptySet;
262        for (Iterator iter = oldSet.iterator(); iter.hasNext(); ) {
263          Variable v = (Variable) iter.next();
264          if (v instanceof FieldDeclaration) {
265            FieldDeclaration f = (FieldDeclaration) v;
266            if (mayAccess(f)) {
267              newSet = newSet.add(f);
268            }
269          }
270        }
271        return newSet;
272      }
273    
274      public SimpleSet ASTNode.removeInstanceVariables(SimpleSet oldSet) {
275        SimpleSet newSet = SimpleSet.emptySet;
276        for (Iterator iter = oldSet.iterator(); iter.hasNext(); ) {
277          Variable v = (Variable) iter.next();
278          if (!v.isInstanceVariable()) {
279            newSet = newSet.add(v);
280          }
281        }
282        return newSet;
283      }
284    
285      /**
286       * @see "JLS $6.6.2.1"
287       * @return true if the expression may access the given field
288       */
289      public boolean Expr.mayAccess(FieldDeclaration f) {
290        if (f.isPublic()) {
291          return true;
292        } else if (f.isProtected()) {
293          if (f.hostPackage().equals(hostPackage())) {
294            return true;
295          }
296          return hostType().mayAccess(this, f);
297        } else if (f.isPrivate()) {
298          return f.hostType().topLevelType() == hostType().topLevelType();
299        } else {
300          return f.hostPackage().equals(hostType().hostPackage());
301        }
302      }
303    
304      /**
305       * @return true if the expression may access the field
306       */
307      public boolean TypeDecl.mayAccess(Expr expr, FieldDeclaration field) {
308        if (instanceOf(field.hostType())) {
309          if (!field.isInstanceVariable()
310              || expr.isSuperAccess()
311              || expr.type().instanceOf(this)) {
312            return true;
313          }
314        }
315    
316        if (isNestedType()) {
317          return enclosingType().mayAccess(expr, field);
318        } else {
319          return false;
320        }
321      }
322    }
323    
324    aspect VariableScopePropagation {
325      interface VariableScope {
326        public SimpleSet lookupVariable(String name);
327      }
328    
329      CatchClause implements VariableScope;
330      Block implements VariableScope;
331      TypeDecl implements VariableScope;
332      ForStmt implements VariableScope;
333    
334      inh Variable Access.unknownField();
335    
336      syn lazy SimpleSet VarAccess.decls() {
337        SimpleSet set = lookupVariable(name());
338        if (set.size() == 1) {
339          Variable v = (Variable) set.iterator().next();
340          if (!isQualified() && inStaticContext()) {
341            if (v.isInstanceVariable() && !hostType().memberFields(v.name()).isEmpty()) {
342              return SimpleSet.emptySet;
343            }
344          } else if (isQualified() && qualifier().staticContextQualifier()) {
345            if (v.isInstanceVariable()) {
346              return SimpleSet.emptySet;
347            }
348          }
349        }
350        return set;
351      }
352      syn lazy Variable VarAccess.decl() {
353        SimpleSet decls = decls();
354        if (decls.size() == 1) {
355          return (Variable) decls.iterator().next();
356        }
357        return unknownField();
358      }
359    }
360    
361    aspect Fields {
362    
363      syn lazy SimpleSet TypeDecl.localFields(String name) =
364          localFieldsMap().containsKey(name)
365          ? (SimpleSet) localFieldsMap().get(name)
366          : SimpleSet.emptySet;
367    
368      syn lazy HashMap TypeDecl.localFieldsMap() {
369        HashMap map = new HashMap();
370        for (int i = 0; i < getNumBodyDecl(); i++) {
371          if (getBodyDecl(i) instanceof FieldDeclaration) {
372            FieldDeclaration decl = (FieldDeclaration) getBodyDecl(i);
373            SimpleSet fields = (SimpleSet) map.get(decl.name());
374            if (fields == null) {
375              fields = SimpleSet.emptySet;
376            }
377            fields = fields.add(decl);
378            map.put(decl.name(), fields);
379          }
380        }
381        return map;
382      }
383      syn lazy HashMap TypeDecl.memberFieldsMap() = localFieldsMap();
384      eq ClassDecl.memberFieldsMap() {
385        HashMap map = new HashMap(localFieldsMap());
386        if (hasSuperclass()) {
387          for (Iterator iter = superclass().fieldsIterator(); iter.hasNext(); ) {
388            FieldDeclaration decl = (FieldDeclaration) iter.next();
389            if (!decl.isPrivate() && decl.accessibleFrom(this) && !localFieldsMap().containsKey(decl.name())) {
390              putSimpleSetElement(map, decl.name(), decl);
391            }
392          }
393        }
394        for (Iterator<TypeDecl> outerIter = interfacesIterator(); outerIter.hasNext(); ) {
395          TypeDecl type = outerIter.next();
396          for (Iterator iter = type.fieldsIterator(); iter.hasNext(); ) {
397            FieldDeclaration decl = (FieldDeclaration) iter.next();
398            if (!decl.isPrivate() && decl.accessibleFrom(this) && !localFieldsMap().containsKey(decl.name())) {
399              putSimpleSetElement(map, decl.name(), decl);
400            }
401          }
402        }
403        return map;
404      }
405      eq InterfaceDecl.memberFieldsMap() {
406        HashMap map = new HashMap(localFieldsMap());
407        for (Iterator<TypeDecl> outerIter = interfacesIterator(); outerIter.hasNext(); ) {
408          TypeDecl typeDecl = outerIter.next();
409          for (Iterator iter = typeDecl.fieldsIterator(); iter.hasNext(); ) {
410            FieldDeclaration f = (FieldDeclaration) iter.next();
411            if (f.accessibleFrom(this) && !f.isPrivate() && !localFieldsMap().containsKey(f.name())) {
412              putSimpleSetElement(map, f.name(), f);
413            }
414          }
415        }
416        return map;
417      }
418      public Iterator TypeDecl.fieldsIterator() {
419        return new Iterator() {
420          private Iterator outer = memberFieldsMap().values().iterator();
421          private Iterator inner = null;
422          public boolean hasNext() {
423            if ((inner == null || !inner.hasNext()) && outer.hasNext()) {
424              inner = ((SimpleSet) outer.next()).iterator();
425            }
426            return inner != null ? inner.hasNext() : false;
427          }
428          public Object next() {
429            return inner.next();
430          }
431          public void remove() { throw new UnsupportedOperationException(); }
432        };
433      }
434    
435      syn lazy SimpleSet TypeDecl.memberFields(String name) = localFields(name);
436    
437      // member fields
438      eq ClassDecl.memberFields(String name) {
439        SimpleSet fields = localFields(name);
440        if (!fields.isEmpty()) {
441          return fields; // this causes hiding of fields in superclass and interfaces
442        }
443        if (hasSuperclass()) {
444          for (Iterator iter = superclass().memberFields(name).iterator(); iter.hasNext(); ) {
445            FieldDeclaration decl = (FieldDeclaration) iter.next();
446            if (!decl.isPrivate() && decl.accessibleFrom(this)) {
447              fields = fields.add(decl);
448            }
449          }
450        }
451        for (Iterator<TypeDecl> outerIter = interfacesIterator(); outerIter.hasNext(); ) {
452          TypeDecl type = outerIter.next();
453          for (Iterator iter = type.memberFields(name).iterator(); iter.hasNext(); ) {
454            FieldDeclaration decl = (FieldDeclaration) iter.next();
455            if (!decl.isPrivate() && decl.accessibleFrom(this)) {
456              fields = fields.add(decl);
457            }
458          }
459        }
460        return fields;
461      }
462    
463      eq InterfaceDecl.memberFields(String name) {
464        SimpleSet fields = localFields(name);
465        if (!fields.isEmpty()) {
466          return fields;
467        }
468        for (Iterator<TypeDecl> outerIter = interfacesIterator(); outerIter.hasNext(); ) {
469          TypeDecl typeDecl = (TypeDecl) outerIter.next();
470          for (Iterator iter = typeDecl.memberFields(name).iterator(); iter.hasNext(); ) {
471            FieldDeclaration f = (FieldDeclaration) iter.next();
472            if (f.accessibleFrom(this) && !f.isPrivate()) {
473              fields = fields.add(f);
474            }
475          }
476        }
477        return fields;
478      }
479    }
480