001    /* Copyright (c) 2005-2008, Torbjorn Ekman
002     *                    2014, Jesper Öqvist <jesper.oqvist@cs.lth.se>
003     * All rights reserved.
004     *
005     * Redistribution and use in source and binary forms, with or without
006     * modification, are permitted provided that the following conditions are met:
007     *
008     * 1. Redistributions of source code must retain the above copyright notice,
009     * this list of conditions and the following disclaimer.
010     *
011     * 2. Redistributions in binary form must reproduce the above copyright notice,
012     * this list of conditions and the following disclaimer in the documentation
013     * and/or other materials provided with the distribution.
014     *
015     * 3. Neither the name of the copyright holder nor the names of its
016     * contributors may be used to endorse or promote products derived from this
017     * software without specific prior written permission.
018     *
019     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
020     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
021     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
022     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
023     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
024     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
025     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
026     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
027     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
028     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
029     * POSSIBILITY OF SUCH DAMAGE.
030     */
031    
032    import java.util.*;
033    import java.util.ArrayList;
034    
035    aspect LookupMethod {
036      inh MethodDecl MethodDecl.unknownMethod();
037      inh MethodDecl MethodAccess.unknownMethod();
038    
039      syn Expr Access.unqualifiedScope() = isQualified() ? nestedScope() : this;
040      inh Expr Access.nestedScope();
041      eq AbstractDot.getRight().nestedScope() = isQualified() ? nestedScope() : this;
042      eq AbstractDot.getLeft().nestedScope() = isQualified() ? nestedScope() : this;
043      eq Program.getChild().nestedScope() { throw new UnsupportedOperationException(); }
044    
045      inh Collection Expr.lookupMethod(String name);
046      inh Collection Stmt.lookupMethod(String name);
047      inh Collection BodyDecl.lookupMethod(String name);
048      inh lazy Collection TypeDecl.lookupMethod(String name);
049    
050      eq MethodAccess.getArg().lookupMethod(String name) = unqualifiedScope().lookupMethod(name);
051      eq ConstructorAccess.getArg().lookupMethod(String name) = unqualifiedScope().lookupMethod(name);
052      eq ArrayAccess.getExpr().lookupMethod(String name) = unqualifiedScope().lookupMethod(name);
053      eq ArrayTypeWithSizeAccess.getExpr().lookupMethod(String name) = unqualifiedScope().lookupMethod(name);
054    
055      eq Program.getChild().lookupMethod(String name) = Collections.EMPTY_LIST;
056      eq TypeDecl.getBodyDecl(int i).lookupMethod(String name) = unqualifiedLookupMethod(name);
057    
058      syn lazy Collection TypeDecl.unqualifiedLookupMethod(String name) {
059        Collection c = memberMethods(name);
060        if (!c.isEmpty()) {
061          return c;
062        }
063        if (isInnerType()) {
064          return lookupMethod(name);
065        }
066        return removeInstanceMethods(lookupMethod(name));
067      }
068    
069      // in explicit constructor invocation
070      eq ConstructorDecl.getParsedConstructorInvocation().lookupMethod(String name) {
071        Collection c = new ArrayList();
072        for (Iterator iter = lookupMethod(name).iterator(); iter.hasNext(); ) {
073          MethodDecl m = (MethodDecl) iter.next();
074          if (!hostType().memberMethods(name).contains(m) || m.isStatic()) {
075            c.add(m);
076          }
077        }
078        return c;
079      }
080    
081      eq ConstructorDecl.getImplicitConstructorInvocation().lookupMethod(String name) {
082        Collection c = new ArrayList();
083        for (Iterator iter = lookupMethod(name).iterator(); iter.hasNext(); ) {
084          MethodDecl m = (MethodDecl) iter.next();
085          if (!hostType().memberMethods(name).contains(m) || m.isStatic()) {
086            c.add(m);
087          }
088        }
089        return c;
090      }
091    
092      public static Collection ASTNode.removeInstanceMethods(Collection c) {
093        c = new LinkedList(c);
094        for (Iterator iter = c.iterator(); iter.hasNext(); ) {
095          MethodDecl m = (MethodDecl) iter.next();
096          if (!m.isStatic()) {
097            iter.remove();
098          }
099        }
100        return c;
101      }
102    
103      eq AbstractDot.getRight().lookupMethod(String name) = getLeft().type().memberMethods(name);
104    
105      syn MethodDecl MethodAccess.singleCandidateDecl() {
106        MethodDecl result = null;
107        for (Iterator iter = lookupMethod(name()).iterator(); iter.hasNext(); ) {
108          MethodDecl m = (MethodDecl) iter.next();
109          if (result == null) {
110            result = m;
111          } else if (m.getNumParameter() == getNumArg() && result.getNumParameter() != getNumArg()) {
112            result = m;
113          }
114        }
115        return result;
116      }
117    
118      protected SimpleSet MethodAccess.maxSpecific(Collection candidates) {
119        SimpleSet maxSpecific = SimpleSet.emptySet;
120        for (Iterator iter = candidates.iterator(); iter.hasNext(); ) {
121          MethodDecl decl = (MethodDecl) iter.next();
122          if (applicable(decl) && accessible(decl)) {
123            if (maxSpecific.isEmpty()) {
124              maxSpecific = maxSpecific.add(decl);
125            } else {
126              MethodDecl other = (MethodDecl) maxSpecific.iterator().next();
127              if (decl.moreSpecificThan(other)) {
128                maxSpecific = decl;
129              } else if (!other.moreSpecificThan(decl)) {
130                maxSpecific = maxSpecific.add(decl);
131              }
132            }
133          }
134        }
135        return maxSpecific;
136      }
137    
138      syn lazy SimpleSet MethodAccess.decls() {
139        SimpleSet maxSpecific = maxSpecific(lookupMethod(name()));
140        if (isQualified() ? qualifier().staticContextQualifier() : inStaticContext()) {
141          maxSpecific = removeInstanceMethods(maxSpecific);
142        }
143        return maxSpecific;
144      }
145    
146      syn lazy MethodDecl MethodAccess.decl() {
147        SimpleSet decls = decls();
148        if (decls.size() == 1) {
149          return (MethodDecl) decls.iterator().next();
150        }
151    
152        // 8.4.6.4 - only return the first method in case of multply inherited abstract methods
153        boolean allAbstract = true;
154        for (Iterator iter = decls.iterator(); iter.hasNext() && allAbstract; ) {
155          MethodDecl m = (MethodDecl) iter.next();
156          if (!m.isAbstract() && !m.hostType().isObject()) {
157            allAbstract = false;
158          }
159        }
160        if (decls.size() > 1 && allAbstract) {
161          return (MethodDecl) decls.iterator().next();
162        }
163        return unknownMethod();
164      }
165      private static SimpleSet MethodAccess.removeInstanceMethods(SimpleSet c) {
166        SimpleSet set = SimpleSet.emptySet;
167        for (Iterator iter = c.iterator(); iter.hasNext(); ) {
168          MethodDecl m = (MethodDecl) iter.next();
169          if (m.isStatic()) {
170            set = set.add(m);
171          }
172        }
173        return set;
174      }
175    }
176    
177    aspect MethodDecl {
178      syn String MethodDecl.name() = getID();
179    
180      // 8.4.2
181      syn lazy String MethodDecl.signature() {
182        StringBuilder sb = new StringBuilder();
183        sb.append(name() + "(");
184        for (int i = 0; i < getNumParameter(); i++) {
185          if (i != 0) {
186            sb.append(", ");
187          }
188          sb.append(getParameter(i).type().typeName());
189        }
190        sb.append(")");
191        return sb.toString();
192      }
193    
194      /**
195       * @return Method signature, including generic parameters.
196       */
197      syn String MethodDecl.fullSignature() {
198        StringBuilder sb = new StringBuilder();
199        sb.append(name() + "(");
200        for (int i = 0; i < getNumParameter(); i++) {
201          if (i != 0) {
202            sb.append(", ");
203          }
204          sb.append(getParameter(i).type().fullName());
205        }
206        sb.append(")");
207        return sb.toString();
208      }
209    
210    
211      /**
212       * 8.4.2 Method Signature
213       * @param other
214       * @return {@code true} if the signature of this method is same as the
215       * the signature of the argument method
216       */
217      syn boolean MethodDecl.sameSignature(MethodDecl other) =
218          signature().equals(other.signature());
219    
220      /**
221       * @param m argument method to compare to
222       * @return {@code true} if this the argument method is less specific than this
223       * and this is not less specific than the argument
224       */
225      syn boolean MethodDecl.moreSpecificThan(MethodDecl m) =
226          m.lessSpecificThan(this) && !this.lessSpecificThan(m);
227    
228      /**
229       * Caution: a less specific than b does not mean b is not less specific than a!
230       * @param m argument method to compare to
231       * @return {@code true} if any parameter of this method decl is not a
232       * subtype (non-proper subtype) of the corresponding parameter of the
233       * argument method
234       */
235      syn lazy boolean MethodDecl.lessSpecificThan(MethodDecl m) {
236        if (getNumParameter() == 0) {
237          return false;
238        }
239        for (int i = 0; i < getNumParameter(); i++) {
240          if (!getParameter(i).type().instanceOf(m.getParameter(i).type())) {
241            return true;
242          }
243        }
244        return false;
245      }
246    
247      public boolean MethodAccess.applicable(MethodDecl decl) {
248        if (getNumArg() != decl.getNumParameter()) {
249          return false;
250        }
251        if (!name().equals(decl.name())) {
252          return false;
253        }
254        for (int i = 0; i < getNumArg(); i++) {
255          if (!getArg(i).type().instanceOf(decl.getParameter(i).type())) {
256            return false;
257          }
258        }
259        return true;
260      }
261    
262      syn boolean MethodAccess.accessible(MethodDecl m) {
263        if (!isQualified()) {
264          return true;
265        }
266        if (!m.accessibleFrom(hostType())) {
267          return false;
268        }
269        // the method is not accessible if the type is not accessible
270        if (!qualifier().type().accessibleFrom(hostType())) {
271          return false;
272        }
273        // 6.6.2.1 -  include qualifier type for protected access
274        if (m.isProtected() && !m.hostPackage().equals(hostPackage())
275            && !m.isStatic() && !qualifier().isSuperAccess()) {
276          return hostType().mayAccess(this, m);
277        }
278        return true;
279      }
280    
281      /**
282       * @return true if the method access may access the method
283       */
284      public boolean TypeDecl.mayAccess(MethodAccess access, MethodDecl method) {
285        if (instanceOf(method.hostType())
286            && access.qualifier().type().instanceOf(this)) {
287          return true;
288        }
289    
290        if (isNestedType()) {
291          return enclosingType().mayAccess(access, method);
292        } else {
293          return false;
294        }
295      }
296    
297      /**
298       * Only check if this method would be able to override other method,
299       * not if this method is declared in a subtype of the hostType of
300       * other method. NB: does not check for equal signature!
301       * @param m other method
302       * @return {@code true} of the method could potentially override
303       */
304      syn lazy boolean MethodDecl.overrideCandidate(MethodDecl m) =
305          !isStatic() && !m.isPrivate() && m.accessibleFrom(hostType());
306    
307      syn lazy boolean MethodDecl.overrides(MethodDecl m) =
308          !isStatic() && !m.isPrivate() && m.accessibleFrom(hostType())
309          && hostType().instanceOf(m.hostType()) && m.signature().equals(signature());
310    
311      syn lazy boolean MethodDecl.hides(MethodDecl m) =
312          isStatic() && !m.isPrivate() && m.accessibleFrom(hostType())
313          && hostType().instanceOf(m.hostType()) && m.signature().equals(signature());
314    }
315    
316    aspect MemberMethods {
317      syn Collection<MethodDecl> TypeDecl.memberMethods(String name) {
318        Collection<MethodDecl> c = methodsNameMap().get(name);
319        if (c != null) {
320          return c;
321        } else {
322          return Collections.emptyList();
323        }
324      }
325      /**
326       * @return map from method name to method declarations
327       */
328      syn lazy Map<String,Collection<MethodDecl>> TypeDecl.methodsNameMap() {
329        Map<String,Collection<MethodDecl>> map = new HashMap<String,Collection<MethodDecl>>();
330        for (Iterator<MethodDecl> iter = methodsIterator(); iter.hasNext(); ) {
331          MethodDecl m = iter.next();
332          Collection<MethodDecl> methods = map.get(m.name());
333          if (methods == null) {
334            methods = new ArrayList<MethodDecl>(4);
335            map.put(m.name(), methods);
336          }
337          methods.add(m);
338        }
339        return map;
340      }
341    
342      /**
343       * Iterate over all local methods in the type.
344       * @return method iterator
345       */
346      public Iterator<MethodDecl> TypeDecl.localMethodsIterator() {
347        return new Iterator<MethodDecl>() {
348          private Iterator<SimpleSet> outer = localMethodsSignatureMap().values().iterator();
349          private Iterator inner = null;
350          public boolean hasNext() {
351            if ((inner == null || !inner.hasNext()) && outer.hasNext()) {
352              inner = outer.next().iterator();
353            }
354            return inner == null ? false : inner.hasNext();
355          }
356          public MethodDecl next() {
357            return (MethodDecl) inner.next();
358          }
359          public void remove() { throw new UnsupportedOperationException(); }
360        };
361      }
362    
363      syn SimpleSet TypeDecl.localMethodsSignature(String signature) {
364        SimpleSet set = localMethodsSignatureMap().get(signature);
365        if (set != null) {
366          return set;
367        }
368        return SimpleSet.emptySet;
369      }
370    
371      /**
372       * @return a mapping of method signature to method declaration
373       */
374      syn lazy Map<String,SimpleSet> TypeDecl.localMethodsSignatureMap() {
375        Map<String,SimpleSet> map = new HashMap<String,SimpleSet>(getNumBodyDecl());
376        for (int i = 0; i < getNumBodyDecl(); i++) {
377          if (getBodyDecl(i) instanceof MethodDecl) {
378            MethodDecl decl = (MethodDecl) getBodyDecl(i);
379            putSimpleSetElement(map, decl.signature(), decl);
380          }
381        }
382        return map;
383      }
384    
385      /**
386       * @return iterator for iterating over all method declarations in implemented
387       * interfaces
388       */
389      public Iterator<MethodDecl> TypeDecl.interfacesMethodsIterator() {
390        return new Iterator<MethodDecl>() {
391          private Iterator<SimpleSet> outer = interfacesMethodsSignatureMap().values().iterator();
392          private Iterator inner = null;
393          public boolean hasNext() {
394            if ((inner == null || !inner.hasNext()) && outer.hasNext()) {
395              inner = outer.next().iterator();
396            }
397            return inner == null ? false : inner.hasNext();
398          }
399          public MethodDecl next() {
400            return (MethodDecl) inner.next();
401          }
402          public void remove() { throw new UnsupportedOperationException(); }
403        };
404      }
405    
406      syn SimpleSet ClassDecl.interfacesMethodsSignature(String signature) {
407        SimpleSet set = interfacesMethodsSignatureMap().get(signature);
408        if (set != null) {
409          return set;
410        } else {
411          return SimpleSet.emptySet;
412        }
413      }
414    
415      /**
416       * Map signature to set of method declarations.
417       */
418      syn lazy Map<String,SimpleSet> TypeDecl.interfacesMethodsSignatureMap() {
419        Map<String,SimpleSet> map = new HashMap<String,SimpleSet>();
420        for (Iterator<TypeDecl> iter = interfacesIterator(); iter.hasNext(); ) {
421          InterfaceDecl iface = (InterfaceDecl) iter.next();
422          for (Iterator<MethodDecl> i2 = iface.localMethodsIterator(); i2.hasNext(); ) {
423            MethodDecl m = i2.next();
424            putSimpleSetElement(map, m.signature(), m);
425          }
426          for (SimpleSet set: iface.interfacesMethodsSignatureMap().values()) {
427            for (Iterator i2 = set.iterator(); i2.hasNext(); ) {
428              MethodDecl m = (MethodDecl) i2.next();
429              putSimpleSetElement(map, m.signature(), m);
430            }
431          }
432        }
433        return map;
434      }
435    
436      /**
437       * Iterate over all member methods in the type.
438       * @return method iterator
439       */
440      public Iterator<MethodDecl> TypeDecl.methodsIterator() {
441        return new Iterator<MethodDecl>() {
442          private Iterator<SimpleSet> outer = methodsSignatureMap().values().iterator();
443          private Iterator inner = null;
444          public boolean hasNext() {
445            if ((inner == null || !inner.hasNext()) && outer.hasNext()) {
446              inner = outer.next().iterator();
447            }
448            return inner != null ? inner.hasNext() : false;
449          }
450          public MethodDecl next() {
451            return (MethodDecl) inner.next();
452          }
453          public void remove() { throw new UnsupportedOperationException(); }
454        };
455      }
456    
457      syn SimpleSet TypeDecl.methodsSignature(String signature) {
458        SimpleSet set = (SimpleSet) methodsSignatureMap().get(signature);
459        if (set != null) {
460          return set;
461        }
462        return SimpleSet.emptySet;
463      }
464    
465      // signature -> SimpleSet
466      syn lazy Map<String,SimpleSet> TypeDecl.methodsSignatureMap() = localMethodsSignatureMap();
467    
468      eq ClassDecl.methodsSignatureMap() {
469        Map<String,SimpleSet> localMap = localMethodsSignatureMap();
470        Map<String,SimpleSet> map = new HashMap<String,SimpleSet>(localMap);
471        if (hasSuperclass()) {
472          for (Iterator<MethodDecl> iter = superclass().methodsIterator(); iter.hasNext(); ) {
473            MethodDecl m = iter.next();
474            if (!m.isPrivate() && m.accessibleFrom(this) && !localMap.containsKey(m.signature())) {
475              putSimpleSetElement(map, m.signature(), m);
476            }
477          }
478        }
479        for (Iterator<MethodDecl> iter = interfacesMethodsIterator(); iter.hasNext(); ) {
480          MethodDecl m = iter.next();
481          if (m.accessibleFrom(this) && !localMap.containsKey(m.signature())) {
482            if (allMethodsAbstract((SimpleSet) map.get(m.signature()))) {
483              putSimpleSetElement(map, m.signature(), m);
484            }
485          }
486        }
487        return map;
488      }
489    
490      eq InterfaceDecl.methodsSignatureMap() {
491        Map<String,SimpleSet> localMap = localMethodsSignatureMap();
492        Map<String,SimpleSet> map = new HashMap<String,SimpleSet>(localMap);
493        for (Iterator<MethodDecl> iter = interfacesMethodsIterator(); iter.hasNext(); ) {
494          MethodDecl m = (MethodDecl) iter.next();
495          if (m.accessibleFrom(this) && !localMap.containsKey(m.signature())) {
496            putSimpleSetElement(map, m.signature(), m);
497          }
498        }
499        for (Iterator<MethodDecl> iter = typeObject().methodsIterator(); iter.hasNext(); ) {
500          MethodDecl m = (MethodDecl) iter.next();
501          if (m.isPublic() && !map.containsKey(m.signature())) {
502            putSimpleSetElement(map, m.signature(), m);
503          }
504        }
505        return map;
506      }
507    
508      /**
509       * Utility method to put a SimpleSet-item in a signature map.
510       */
511      protected static void ASTNode.putSimpleSetElement(Map<String,SimpleSet> map, String key, SimpleSet value) {
512        SimpleSet set = map.get(key);
513        if (set == null) {
514          set = SimpleSet.emptySet;
515        }
516        map.put(key, set.add(value));
517      }
518    
519      protected boolean TypeDecl.allMethodsAbstract(SimpleSet set) {
520        if (set == null) {
521          return true;
522        }
523        for (Iterator iter = set.iterator(); iter.hasNext(); ) {
524          MethodDecl m = (MethodDecl) iter.next();
525          if (!m.isAbstract()) {
526            return false;
527          }
528        }
529        return true;
530      }
531    }
532    
533    aspect AncestorMethods {
534      // methods with the same signature declared in ancestors
535      // this is used when checking correct overriding, hiding, and implementation of abstract methods
536      syn lazy SimpleSet TypeDecl.ancestorMethods(String signature) = SimpleSet.emptySet;
537    
538      eq ClassDecl.ancestorMethods(String signature) {
539        SimpleSet set = SimpleSet.emptySet;
540        if (hasSuperclass()) {
541          for (Iterator iter = superclass().localMethodsSignature(signature).iterator(); iter.hasNext(); ) {
542            MethodDecl m = (MethodDecl) iter.next();
543            if (!m.isPrivate()) {
544              set = set.add(m);
545            }
546          }
547        }
548        // always add interface methods to the ancestorMethods set so that their
549        // access modifiers are checked against local overriding methods
550        for (Iterator iter = interfacesMethodsSignature(signature).iterator(); iter.hasNext(); ) {
551          MethodDecl m = (MethodDecl) iter.next();
552          set = set.add(m);
553        }
554        if (!hasSuperclass()) {
555          return set;
556        }
557        if (set.size() == 1) {
558          MethodDecl m = (MethodDecl) set.iterator().next();
559          if (!m.isAbstract()) {
560            boolean done = true;
561            for (Iterator iter = superclass().ancestorMethods(signature).iterator(); iter.hasNext(); ) {
562              MethodDecl n = (MethodDecl) iter.next();
563              if (n.isPrivate() || !n.accessibleFrom(m.hostType())) {
564                done = false;
565              }
566            }
567            if (done) {
568              return set;
569            }
570          }
571        }
572        for (Iterator iter = superclass().ancestorMethods(signature).iterator(); iter.hasNext(); ) {
573          MethodDecl m = (MethodDecl) iter.next();
574          set = set.add(m);
575        }
576        return set;
577      }
578    
579      eq InterfaceDecl.ancestorMethods(String signature) {
580        SimpleSet set = SimpleSet.emptySet;
581        for (Iterator<TypeDecl> outerIter = interfacesIterator(); outerIter.hasNext(); ) {
582          TypeDecl typeDecl = outerIter.next();
583          for (Iterator iter = typeDecl.methodsSignature(signature).iterator(); iter.hasNext(); ) {
584            MethodDecl m = (MethodDecl) iter.next();
585            set = set.add(m);
586          }
587        }
588        if (!interfacesIterator().hasNext()) {
589          for (Iterator iter = typeObject().methodsSignature(signature).iterator(); iter.hasNext(); ) {
590            MethodDecl m = (MethodDecl) iter.next();
591            if (m.isPublic()) {
592              set = set.add(m);
593            }
594          }
595        }
596        return set;
597      }
598    }