001    /* Copyright (c) 2014, Erik Hogeman <Erik.Hogemn@gmail.com>
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     *     * Redistributions of source code must retain the above copyright notice,
008     *       this list of conditions and the following disclaimer.
009     *     * Redistributions in binary form must reproduce the above copyright
010     *       notice, this list of conditions and the following disclaimer in the
011     *       documentation and/or other materials provided with the distribution.
012     *     * Neither the name of the Lund University nor the names of its
013     *       contributors may be used to endorse or promote products derived from
014     *       this software without specific prior written permission.
015     *
016     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
017     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
018     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
019     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
020     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
021     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
022     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
023     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
024     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
025     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
026     * POSSIBILITY OF SUCH DAMAGE.
027     */
028    aspect FunctionalInterface {
029    
030      syn lazy boolean TypeDecl.isFunctionalInterface() = false;
031      eq InterfaceDecl.isFunctionalInterface() = isFunctional();
032    
033      syn lazy boolean InterfaceDecl.isFunctional();
034    
035      //8.4.2
036      syn boolean GenericMethodDecl.sameSignature(MethodDecl m) {
037        if (!m.isGeneric()) {
038          return false;
039        }
040        GenericMethodDecl gm = m.genericDecl();
041        return !(!name().equals(gm.name()) || !sameTypeParameters(gm) || !sameFormalParameters(gm));
042      }
043    
044      refine MethodDecl
045      eq MethodDecl.fullSignature() {
046        StringBuilder sb = new StringBuilder();
047        sb.append(name() + "(");
048        for (int i = 0; i < getNumParameter(); i++) {
049          if (i != 0) {
050            sb.append(", ");
051          }
052          if (getParameter(i).type() instanceof PrimitiveType) {
053            sb.append(getParameter(i).type().typeName());
054          } else {
055            sb.append(getParameter(i).type().fullName());
056          }
057        }
058        sb.append(")");
059        return sb.toString();
060      }
061    
062      syn lazy boolean MethodDecl.subsignatureTo(MethodDecl m) {
063        if (fullSignature().equals(m.fullSignature())) {
064          return true;
065        } else if (fullSignature().equals(m.signature())) {
066          return true;
067        } else {
068          return false;
069        }
070      }
071    
072      syn lazy boolean GenericMethodDecl.subsignatureTo(MethodDecl m) {
073        if (m.isGeneric()) {
074          GenericMethodDecl gm = m.genericDecl();
075          if (this == gm) {
076            return true;
077          }
078          return sameSignature(gm);
079        } else {
080          // A generic method can never be subsignature to a non-generic method.
081          return false;
082        }
083      }
084    
085      //8.4.5
086      syn lazy boolean MethodDecl.returnTypeSubstitutableFor(MethodDecl m) {
087        TypeDecl t1 = type();
088        TypeDecl t2 = m.type();
089        if (t1 instanceof VoidType && t2 instanceof VoidType) {
090          return true;
091        } else if (t1 instanceof PrimitiveType && t2 instanceof PrimitiveType) {
092          PrimitiveType p1 = (PrimitiveType)t1;
093          PrimitiveType p2 = (PrimitiveType)t2;
094          return p1 == p2;
095        } else if (t1.isReferenceType() && t2.isReferenceType()) {
096          if (t1.strictSubtype(t2)) {
097            return true;
098          } else {
099            return t1 == t2.erasure();
100          }
101        } else {
102          return false;
103        }
104      }
105    
106    
107      //8.4.4
108      syn lazy boolean GenericMethodDecl.sameTypeParameters(GenericMethodDecl gm) {
109        if (getNumTypeParameter() != gm.getNumTypeParameter()) {
110          return false;
111        }
112    
113        for (int i = 0; i < getNumTypeParameter(); i++) {
114          TypeVariable tv1 = getTypeParameter(i);
115          TypeVariable tv2 = gm.getTypeParameter(i);
116          if (tv1.getNumTypeBound() != tv2.getNumTypeBound()) {
117            return false;
118          }
119    
120          /* The bounds have to be the same in the way that a bound
121          that exists in type variable tv1 must exist exactly the same
122          number of times in tv2, but the order doesn't matter */
123    
124          boolean[] checkedBound = new boolean[tv1.getNumTypeBound()];
125    
126          for (int j = 0; j < tv1.getNumTypeBound(); j++) {
127            boolean found = false;
128            for (int k = 0; k < tv2.getNumTypeBound(); k++) {
129              if (checkedBound[k]) {
130                continue;
131              }
132              Access a1 = tv1.getTypeBound(j);
133              Access a2 = tv2.getTypeBound(k);
134    
135              if (a1.sameType(a2)) {
136                checkedBound[k] = true;
137                found = true;
138                break;
139              }
140            }
141            if (!found) {
142              return false;
143            }
144          }
145        }
146        return true;
147      }
148    
149      //8.4.2
150      syn lazy boolean GenericMethodDecl.sameFormalParameters(GenericMethodDecl gm) {
151        if (getNumParameter() != gm.getNumParameter()) {
152          return false;
153        }
154        if (getNumParameter() == 0) {
155          return true;
156        }
157    
158        for (int i = 0; i < getNumParameter(); i++) {
159          ParameterDeclaration p1 = getParameter(i);
160          ParameterDeclaration p2 = gm.getParameter(i);
161          Access a1 = p1.getTypeAccess();
162          Access a2 = p2.getTypeAccess();
163          if (!a1.sameType(a2)) {
164            return false;
165          }
166        }
167        return true;
168      }
169    
170      /*
171      Checks that two type accesses are the same, while taking type variable substitution
172      into account.
173      */
174      public boolean Access.sameType(Access a) {
175        if (this instanceof ArrayTypeAccess && a instanceof ArrayTypeAccess) {
176          ArrayTypeAccess at1 = (ArrayTypeAccess) this;
177          ArrayTypeAccess at2 = (ArrayTypeAccess) a;
178          return at1.sameType(at2);
179        } else if (this instanceof AbstractWildcard && a instanceof AbstractWildcard) {
180          AbstractWildcard w1 = (AbstractWildcard) this;
181          AbstractWildcard w2 = (AbstractWildcard) a;
182          return w1.sameType(w2);
183        } else if (this instanceof TypeAccess && a instanceof TypeAccess) {
184          TypeAccess t1 = (TypeAccess) this;
185          TypeAccess t2 = (TypeAccess) a;
186          return t1.sameType(t2);
187        } else if (this instanceof ParTypeAccess && a instanceof ParTypeAccess) {
188          ParTypeAccess pta1 = (ParTypeAccess) this;
189          ParTypeAccess pta2 = (ParTypeAccess) a;
190          return pta1.sameType(pta2);
191        } else {
192          return false;
193        }
194      }
195    
196      public boolean TypeAccess.sameType(TypeAccess t) {
197        /*
198        First, two type variables that are to be compared are checked to see if they are
199        both declared by methods and that one of the methods is not inside the scope of the other
200        method. If this is the case it is enough to simply check that the positions are equal.
201        Otherwise the types has to equal.
202        */
203    
204        if (type() instanceof TypeVariable && t.type() instanceof TypeVariable) {
205          TypeVariable t1 = (TypeVariable) type();
206          TypeVariable t2 = (TypeVariable) t.type();
207          if (t1.typeVarInMethod() && t2.typeVarInMethod()
208              && t1.genericMethodLevel() == t2.genericMethodLevel()) {
209            return t1.typeVarPosition() == t2.typeVarPosition();
210          } else {
211            return t1 == t2;
212          }
213        } else if (type() instanceof TypeVariable && !(t.type() instanceof TypeVariable)
214            || t.type() instanceof TypeVariable && !(type() instanceof TypeVariable) ) {
215          return false;
216        } else if (type() == t.type()) {
217          return true;
218        } else {
219          return false;
220        }
221      }
222    
223      public boolean ParTypeAccess.sameType(ParTypeAccess p) {
224        TypeAccess ta1 = (TypeAccess) getTypeAccess();
225        TypeAccess ta2 = (TypeAccess) p.getTypeAccess();
226        if (!ta1.sameType(ta2)) {
227          return false;
228        }
229    
230        if (getNumTypeArgument() != p.getNumTypeArgument()) {
231          return false;
232        }
233    
234        for (int i = 0; i < getNumTypeArgument(); i++) {
235          Access a1 = getTypeArgument(i);
236          Access a2 = p.getTypeArgument(i);
237          if (!a1.sameType(a2)) {
238            return false;
239          }
240        }
241    
242        return true;
243      }
244    
245      public boolean AbstractWildcard.sameType(AbstractWildcard w) {
246        if (this instanceof Wildcard && w instanceof Wildcard) {
247          return true;
248        } else if (this instanceof WildcardExtends && w instanceof WildcardExtends) {
249          Access a1 = ((WildcardExtends) this).getAccess();
250          Access a2 = ((WildcardExtends) w).getAccess();
251          return a1.sameType(a2);
252        } else if (this instanceof WildcardSuper && w instanceof WildcardSuper) {
253          Access a1 = ((WildcardSuper) this).getAccess();
254          Access a2 = ((WildcardSuper) w).getAccess();
255          return a1.sameType(a2);
256        } else {
257          return false;
258        }
259      }
260    
261      public boolean ArrayTypeAccess.sameType(ArrayTypeAccess a) {
262        ArrayTypeAccess at1 = this;
263        ArrayTypeAccess at2 = a;
264        while (true) {
265          Access a1 = at1.getAccess();
266          Access a2 = at2.getAccess();
267          if (a1 instanceof ArrayTypeAccess && a2 instanceof ArrayTypeAccess) {
268            at1 = (ArrayTypeAccess) a1;
269            at2 = (ArrayTypeAccess) a2;
270            continue;
271          } else {
272            return a1.sameType(a2);
273          }
274        }
275      }
276    
277      // 9.8
278      eq InterfaceDecl.isFunctional() {
279        LinkedList<MethodDecl> methods = collectAbstractMethods();
280        boolean foundMethod = false;
281    
282        if (methods.size() == 0) {
283          return false;
284        } else if (methods.size() == 1) {
285          return true;
286        } else {
287          for (MethodDecl current : methods) {
288            foundMethod = true;
289            for (MethodDecl inner : methods) {
290              if (!current.subsignatureTo(inner) || !current.returnTypeSubstitutableFor(inner)) {
291                foundMethod = false;
292              }
293            }
294            if (foundMethod) {
295              break;
296            }
297          }
298        }
299        return foundMethod;
300      }
301    
302      eq ParInterfaceDecl.isFunctional() = ((InterfaceDecl) original()).isFunctional();
303    
304      //9.8
305      syn lazy LinkedList<MethodDecl> InterfaceDecl.collectAbstractMethods() {
306        LinkedList<MethodDecl> methods = new LinkedList<MethodDecl>();
307        Map<String, SimpleSet> map = localMethodsSignatureMap();
308        Map<String, SimpleSet> objectMethods = typeObject().methodsSignatureMap();
309        MethodDecl inObject;
310    
311        for (Map.Entry<String,SimpleSet> entry: map.entrySet()) {
312          SimpleSet set = entry.getValue();
313          MethodDecl m = (MethodDecl) set.iterator().next();
314    
315          SimpleSet objectSet = objectMethods.get(m.signature());
316          if (m.isAbstract()) {
317            if (objectSet == null || objectSet.isEmpty()) {
318              methods.add(m);
319            } else {
320              inObject = (MethodDecl) objectSet.iterator().next();
321              if (!inObject.isPublic()) {
322                methods.add(m);
323              }
324            }
325          }
326        }
327    
328        for (Iterator outerIter = interfacesIterator(); outerIter.hasNext();) {
329          TypeDecl typeDecl = (TypeDecl) outerIter.next();
330          for (Iterator iter = typeDecl.methodsIterator(); iter.hasNext();) {
331            MethodDecl m = (MethodDecl) iter.next();
332    
333            if (m.isAbstract() && !m.isPrivate() && m.accessibleFrom(this)) {
334              SimpleSet objectSet = objectMethods.get(m.signature());
335              if (objectSet == null || objectSet.isEmpty()) {
336                methods.add(m);
337              } else {
338                inObject = (MethodDecl) objectSet.iterator().next();
339                if (!inObject.isPublic()) {
340                  methods.add(m);
341                }
342              }
343            }
344          }
345        }
346        return methods;
347      }
348    
349      eq GenericMethodDecl.usesTypeVariable() =
350          super.usesTypeVariable() || getTypeParameterList().usesTypeVariable();
351    
352        /*List<Access> GenericMethodDecl.substituteTypeParameters(List<Access> typeParameters, Parameterization parTypeDecl) {
353          List l = new List<Access>();
354          for (Access a : typeParameters) {
355            l.add(a.type().substitute(parTypeDecl));
356          }
357          return l;
358        }
359    
360        refine LookupParTypeDecl
361      public BodyDecl GenericMethodDecl.substitutedBodyDecl(Parameterization parTypeDecl) {
362        //System.out.println("Begin substituting generic " + signature() + " in " + hostType().typeName() + " with " + parTypeDecl.typeSignature());
363        List tmpList = getTypeParameterList().treeCopyNoTransform();
364        for (Object o : tmpList) {
365          TypeVariable v = (TypeVariable)o;
366          v.setTypeBoundList(substituteTypeParameters(v.getTypeBoundList(), parTypeDecl));
367        }
368    
369        GenericMethodDecl m = new GenericMethodDecl(
370          (Modifiers)getModifiers().treeCopyNoTransform(),
371          getTypeAccess().type().substituteReturnType(parTypeDecl),
372          getID(),
373          getParameterList().substitute(parTypeDecl),
374          getExceptionList().substitute(parTypeDecl),
375          new Opt(),
376          tmpList
377        );
378        m.original = this;
379        //System.out.println("End substituting generic " + signature());
380        return m;
381      }*/
382    }