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 FunctionDescriptor {
029      class FunctionDescriptor {
030        ArrayList<TypeDecl> throwsList = null;
031        MethodDecl method = null;
032        InterfaceDecl fromInterface = null;
033    
034        public FunctionDescriptor(InterfaceDecl fromInterface) {
035          this.fromInterface = fromInterface;
036        }
037    
038        public boolean isGeneric() {
039          if (method == null) {
040            return false;
041          } else {
042            return method.isGeneric();
043          }
044        }
045    
046        public InterfaceDecl fromInterface() {
047          return this.fromInterface;
048        }
049    
050        public String toString() {
051          StringBuilder str = new StringBuilder();
052          if (method != null) {
053            if (method.isGeneric()) {
054              GenericMethodDecl genericMethod = method.genericDecl();
055              str.append("<" + genericMethod.getTypeParameter(0).prettyPrint());
056              for (int i = 1; i < genericMethod.getNumTypeParameter(); i++) {
057                str.append(", " + genericMethod.getTypeParameter(i).prettyPrint());
058              }
059              str.append("> ");
060            }
061            str.append("(");
062            if (method.getNumParameter() > 0) {
063              str.append(method.getParameter(0).type().typeName());
064              for (int i = 1; i < method.getNumParameter(); i++) {
065                str.append(", " + method.getParameter(i).type().typeName());
066              }
067            }
068            str.append(")->");
069            str.append(method.type().typeName());
070    
071            str.append(" throws ");
072            if (throwsList.size() > 0) {
073              str.append(throwsList.get(0).typeName());
074              for (int i = 1; i < throwsList.size(); i++) {
075                str.append(", " + throwsList.get(i).typeName());
076              }
077            }
078          }
079    
080          return str.toString();
081        }
082      }
083    
084      syn lazy boolean InterfaceDecl.hasFunctionDescriptor() {
085        return functionDescriptor() != null;
086      }
087    
088      syn lazy FunctionDescriptor ParInterfaceDecl.functionDescriptor() {
089        if (getNumArgument() != ((GenericInterfaceDecl)original()).getNumTypeParameter()) {
090          return null;
091        } else {
092          return super.functionDescriptor();
093        }
094      }
095    
096      // 9.8
097      syn lazy FunctionDescriptor InterfaceDecl.functionDescriptor() {
098        LinkedList<MethodDecl> methods = collectAbstractMethods();
099    
100        if (methods.size() == 0) {
101          return null;
102        } else if (methods.size() == 1) {
103          MethodDecl m = methods.getFirst();
104          FunctionDescriptor f = new FunctionDescriptor(this);
105          f.method = m;
106          ArrayList<TypeDecl> throwsList = new ArrayList<TypeDecl>();
107          for (Access exception : m.getExceptionList()) {
108            throwsList.add(exception.type());
109          }
110          f.throwsList = throwsList;
111          return f;
112        } else {
113          FunctionDescriptor f = null;
114          MethodDecl foundMethod = null;
115    
116          for (MethodDecl current : methods) {
117            foundMethod = current;
118            for (MethodDecl inner : methods) {
119              if (!current.subsignatureTo(inner) || !current.returnTypeSubstitutableFor(inner)) {
120                foundMethod = null;
121              }
122            }
123            if (foundMethod != null) {
124              break;
125            }
126          }
127    
128          ArrayList<Access> descriptorThrows = new ArrayList<Access>();
129          if (foundMethod != null) {
130            // Now the throws-list needs to be computed as stated in 9.8
131            for (MethodDecl current : methods) {
132              for (Access exception : current.getExceptionList()) {
133                boolean alreadyInserted = false;
134                for (Access found : descriptorThrows) {
135                  if (found.sameType(exception)) {
136                    alreadyInserted = true;
137                    break;
138                  }
139                }
140                if (alreadyInserted) {
141                  continue;
142                }
143    
144                boolean foundIncompatibleClause = false;
145                // Has to be the subtype to at least one exception in each clause
146                if (foundMethod.isGeneric()) {
147                  for (MethodDecl inner : methods) {
148                    if (!inner.subtypeThrowsClause(exception)) {
149                      foundIncompatibleClause = true;
150                      break;
151                    }
152                  }
153                } else {
154                  for (MethodDecl inner : methods) {
155                    if (!inner.subtypeThrowsClauseErased(exception)) {
156                      foundIncompatibleClause = true;
157                      break;
158                    }
159                  }
160                }
161    
162                if (!foundIncompatibleClause) {
163                  // Was subtype to one exception in every clause
164                  descriptorThrows.add(exception);
165                }
166              }
167            }
168    
169            /* Found a suitable method and finished building throws-list,
170            now the descriptor just needs to be put together */
171            f = new FunctionDescriptor(this);
172            f.method = foundMethod;
173            if (descriptorThrows.size() == 0) {
174              f.throwsList = new ArrayList<TypeDecl>();
175            } else {
176              ArrayList<TypeDecl> throwsList = new ArrayList<TypeDecl>();
177    
178              /* All type variables must be replaced with foundMethods
179                  type variables if the descriptor is generic */
180              if (foundMethod.isGeneric()) {
181                GenericMethodDecl foundGeneric = foundMethod.genericDecl();
182                for (Access exception : descriptorThrows) {
183                  if (exception.type() instanceof TypeVariable) {
184                    TypeVariable foundVar = (TypeVariable) exception.type();
185                    TypeVariable original = foundGeneric.getTypeParameter(foundVar.typeVarPosition());
186                    throwsList.add(original);
187                  } else {
188                    throwsList.add(exception.type());
189                  }
190                }
191              } else {
192                // All throwed types must be erased if the descriptor is not generic.
193                for (Access exception : descriptorThrows) {
194                  throwsList.add(exception.type().erasure());
195                }
196              }
197              f.throwsList = throwsList;
198            }
199          }
200          return f;
201        }
202      }
203    
204      /**
205       * Checks that the argument exception is a subtype to all exceptions
206       * in the methods throws-clause. This takes the position of the type
207       * parameters into account.
208       */
209      public boolean MethodDecl.subtypeThrowsClause(Access exception) {
210        boolean foundCompatible = false;
211        for (Access throwsException : getExceptionList()) {
212          if (exception.type().strictSubtype(throwsException.type())) {
213            foundCompatible = true;
214            break;
215          }
216        }
217        return foundCompatible;
218      }
219    
220      /**
221       * Checks that the argument exception is a subtype to all exceptions
222       * in the methods throws-clause. Performs erasure on all types before
223       * comparing them.
224       */
225      public boolean MethodDecl.subtypeThrowsClauseErased(Access exception) {
226        boolean foundCompatible = false;
227        for (Access throwsException : getExceptionList()) {
228          if (exception.type().erasure().strictSubtype(throwsException.type().erasure())) {
229            foundCompatible = true;
230            break;
231          }
232        }
233        return foundCompatible;
234      }
235    
236    }