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 MethodReference {
029    
030      inh MethodDecl MethodReference.unknownMethod();
031    
032      syn lazy MethodDecl ExprMethodReference.targetMethod(FunctionDescriptor f) {
033        return syntheticMethodAccess(f).decl();
034      }
035    
036      syn nta lazy Access ExprMethodReference.syntheticAccess(FunctionDescriptor f) {
037        List<Expr> arguments = new List<Expr>();
038        for (int i = 0; i < f.method.getNumParameter(); i++) {
039          TypeDecl argumentType = f.method.getParameter(i).type();
040          arguments.add(new SyntheticTypeAccess(argumentType));
041        }
042    
043        if (!hasTypeArgument()) {
044          MethodReferenceAccess mAccess = new MethodReferenceAccess(name(), arguments, f);
045          return ((Expr) getExpr().treeCopyNoTransform()).qualifiesAccess(mAccess);
046        } else {
047          ParMethodReferenceAccess pmAccess = new ParMethodReferenceAccess(name(), arguments,
048              (List<Access>) getTypeArgumentList().treeCopyNoTransform(), f);
049          return ((Expr) getExpr().treeCopyNoTransform()).qualifiesAccess(pmAccess);
050        }
051      }
052    
053      syn lazy MethodAccess ExprMethodReference.syntheticMethodAccess(FunctionDescriptor f) {
054        Access synAccess = syntheticAccess(f);
055        return (MethodAccess) synAccess.lastAccess();
056      }
057    
058      /* Type analysis for TypeMethodReferences are done below here.
059       * The two different types of method accesses being used here,
060       * called 'static' and 'instance', are the results of the two
061       * different lookups that are to be done in the case of a type
062       * being used in a method reference. The details can be found
063       * in JLS version 8, section 15.13.1
064       */
065    
066      syn lazy MethodDecl TypeMethodReference.targetStaticMethod(FunctionDescriptor f) {
067        MethodAccess synAcc = syntheticStaticMethodAccess(f);
068        SimpleSet maxSpecific = synAcc.maxSpecific(synAcc.lookupMethod(synAcc.name()));
069        if (maxSpecific.size() == 1) {
070          return (MethodDecl) maxSpecific.iterator().next();
071        } else {
072          return unknownMethod();
073        }
074      }
075    
076      syn lazy MethodDecl TypeMethodReference.targetInstanceMethod(FunctionDescriptor f) {
077        if (f.method.getNumParameter() == 0
078            || !f.method.getParameter(0).type().strictSubtype(getTypeAccess().type())) {
079          return unknownMethod();
080        }
081    
082        MethodAccess synAcc = syntheticInstanceMethodAccess(f);
083        SimpleSet maxSpecific = synAcc.maxSpecific(synAcc.lookupMethod(synAcc.name()));
084        if (maxSpecific.size() == 1) {
085          return (MethodDecl) maxSpecific.iterator().next();
086        } else {
087          return unknownMethod();
088        }
089      }
090    
091      syn lazy boolean TypeMethodReference.validStaticMethod(FunctionDescriptor f) {
092        MethodDecl decl = targetStaticMethod(f);
093        return !(decl == unknownMethod() || !decl.isStatic());
094      }
095    
096      syn lazy boolean TypeMethodReference.validInstanceMethod(FunctionDescriptor f) {
097        MethodDecl decl = targetInstanceMethod(f);
098        return !(decl == unknownMethod() || decl.isStatic());
099      }
100    
101      /* This attribute is used for method references when the reference type must be inferred.  The
102       * argument list declared in the parameter must be used, so in order to make the type lookup work
103       * this attribute is an nta. It must be located here in order to not miss type variables declared
104       * by the method where this parameter is located.
105       */
106      syn nta lazy ParTypeAccess ParameterDeclaration.inferredReferenceAccess(TypeAccess typeAccess) {
107        if (!(getTypeAccess() instanceof ParTypeAccess)) {
108          return new ParTypeAccess((TypeAccess) typeAccess.treeCopyNoTransform(), new List<Access>());
109        }
110        ParTypeAccess parTypeAccess = (ParTypeAccess)getTypeAccess();
111        return new ParTypeAccess((TypeAccess) typeAccess.treeCopyNoTransform(),
112            (List<Access>) parTypeAccess.getTypeArgumentList().treeCopyNoTransform());
113      }
114    
115      /* When type method references are declared using a raw type, the type arguments can sometimes be
116       * inferred by looking at the target function descriptor. This attribute infers the type where the
117       * method lookup should be done. Returns null if no inferred type should be used.
118       */
119      syn lazy TypeDecl TypeMethodReference.inferredReferenceType(FunctionDescriptor f) {
120        if (f.method.getNumParameter() == 0) {
121          return null;
122        } else if (!(f.method.getParameter(0).getTypeAccess() instanceof ParTypeAccess)) {
123          return null;
124        } else if (!getTypeAccess().type().isRawType() || !(getTypeAccess() instanceof TypeAccess)) {
125          return null;
126        }
127    
128        ParameterDeclaration param = f.method.getParameter(0);
129        if (!param.type().strictSubtype(param
130              .inferredReferenceAccess((TypeAccess) getTypeAccess()).type())) {
131          return null;
132        }
133        return param.inferredReferenceAccess((TypeAccess) getTypeAccess()).type();
134      }
135    
136      syn nta lazy Access TypeMethodReference.syntheticStaticAccess(FunctionDescriptor f) {
137        List<Expr> arguments = new List<Expr>();
138        for (int i = 0; i < f.method.getNumParameter(); i++) {
139          TypeDecl argumentType = f.method.getParameter(i).type();
140          arguments.add(new SyntheticTypeAccess(argumentType));
141        }
142    
143        if (!hasTypeArgument()) {
144          MethodReferenceAccess mAccess = new MethodReferenceAccess(name(), arguments, f);
145          return ((Access) getTypeAccess().treeCopyNoTransform()).qualifiesAccess(mAccess);
146        } else {
147          ParMethodReferenceAccess pmAccess = new ParMethodReferenceAccess(name(), arguments,
148              (List<Access>) getTypeArgumentList().treeCopyNoTransform(), f);
149          return ((Access) getTypeAccess().treeCopyNoTransform()).qualifiesAccess(pmAccess);
150        }
151      }
152    
153      syn lazy MethodAccess TypeMethodReference.syntheticStaticMethodAccess(FunctionDescriptor f) {
154        Access synAccess = syntheticStaticAccess(f);
155        return (MethodAccess) synAccess.lastAccess();
156      }
157    
158      syn nta lazy Access TypeMethodReference.syntheticInstanceAccess(FunctionDescriptor f) {
159        List<Expr> arguments = new List<Expr>();
160        for (int i = 1; i < f.method.getNumParameter(); i++) {
161          TypeDecl argumentType = f.method.getParameter(i).type();
162          arguments.add(new SyntheticTypeAccess(argumentType));
163        }
164    
165        Access qualifier = null;
166    
167        if (inferredReferenceType(f) != null) {
168          qualifier = new SyntheticTypeAccess(inferredReferenceType(f));
169        } else {
170          qualifier = (Access) getTypeAccess().treeCopyNoTransform();
171        }
172    
173        if (!hasTypeArgument()) {
174          MethodReferenceAccess mAccess = new MethodReferenceAccess(name(), arguments, f);
175          return qualifier.qualifiesAccess(mAccess);
176        } else {
177          ParMethodReferenceAccess pmAccess = new ParMethodReferenceAccess(name(), arguments,
178              (List<Access>) getTypeArgumentList().treeCopyNoTransform(), f);
179          return qualifier.qualifiesAccess(pmAccess);
180        }
181      }
182    
183      syn lazy MethodAccess TypeMethodReference.syntheticInstanceMethodAccess(FunctionDescriptor f) {
184        Access synAccess = syntheticInstanceAccess(f);
185        return (MethodAccess) synAccess.lastAccess();
186      }
187    
188      /*
189      Below is the code which rewrites ambiguous method references to the
190      correct type. Also ParseNames in method references get their correct
191      NameType set below here.
192      */
193    
194      eq MethodReference.getTypeArgument().nameType() = NameType.TYPE_NAME;
195      eq TypeMethodReference.getTypeAccess().nameType() = NameType.TYPE_NAME;
196      eq AmbiguousMethodReference.getAmbiguousName().nameType() = NameType.AMBIGUOUS_NAME;
197    
198      rewrite AmbiguousMethodReference {
199        when (!getAmbiguousName().isTypeAccess())
200          to ExprMethodReference {
201            return new ExprMethodReference(getTypeArgumentList(), getID(), getAmbiguousName());
202          }
203      }
204    
205      rewrite AmbiguousMethodReference {
206        when (getAmbiguousName().isTypeAccess())
207          to TypeMethodReference {
208            return new TypeMethodReference(getTypeArgumentList(), getID(), getAmbiguousName());
209          }
210      }
211    
212      syn lazy boolean MethodReference.congruentTo(FunctionDescriptor f);
213    
214      eq ExprMethodReference.congruentTo(FunctionDescriptor f) {
215        MethodDecl decl = targetMethod(f);
216        if (unknownMethod() == decl) {
217          return false;
218        }
219        if (f.method.type().isVoid()) {
220          return true;
221        }
222        if (decl.type().isVoid()) {
223          return false;
224        }
225        return decl.type().assignConversionTo(f.method.type(), null);
226      }
227    
228      eq TypeMethodReference.congruentTo(FunctionDescriptor f) {
229        MethodDecl staticMethod = targetStaticMethod(f);
230        MethodDecl instanceMethod = targetInstanceMethod(f);
231        if (unknownMethod() != staticMethod && unknownMethod() != instanceMethod) {
232          return false;
233        } else if (unknownMethod() == staticMethod && unknownMethod() == instanceMethod) {
234          return false;
235        }
236        MethodDecl found;
237        if (unknownMethod() != staticMethod) {
238          found = staticMethod;
239        } else {
240          found = instanceMethod;
241        }
242        if (f.method.type().isVoid()) {
243          return true;
244        }
245        if (found.type().isVoid()) {
246          return false;
247        }
248        return found.type().assignConversionTo(f.method.type(), null);
249      }
250    
251      eq AmbiguousMethodReference.congruentTo(FunctionDescriptor f) = false;
252    
253      syn lazy ArrayList<MethodDecl> MethodReference.potentiallyApplicableMethods(FunctionDescriptor f);
254    
255      eq ExprMethodReference.potentiallyApplicableMethods(FunctionDescriptor f) {
256        Collection<MethodDecl> col = getExpr().type().memberMethods(name());
257        ArrayList<MethodDecl> applicable = new ArrayList<MethodDecl>();
258        for (MethodDecl decl : col) {
259          if (!decl.accessibleFrom(hostType())) {
260            continue;
261          }
262          if (!(decl.arity() == f.method.arity())) {
263            continue;
264          }
265          if (hasTypeArgument()) {
266            if (!decl.isGeneric()) {
267              continue;
268            }
269            GenericMethodDecl genDecl = decl.genericDecl();
270            if (!(getNumTypeArgument() == genDecl.getNumTypeParameter())) {
271            }
272              continue;
273          }
274          applicable.add(decl);
275        }
276        return applicable;
277      }
278    
279      eq TypeMethodReference.potentiallyApplicableMethods(FunctionDescriptor f) {
280        Collection<MethodDecl> col = getTypeAccess().type().memberMethods(name());
281        ArrayList<MethodDecl> applicable = new ArrayList<MethodDecl>();
282        for (MethodDecl decl : col) {
283          if (!decl.accessibleFrom(hostType())) {
284            continue;
285          }
286          if (!(decl.arity() == f.method.arity()) && !(decl.arity() == f.method.arity() - 1)) {
287            continue;
288          }
289          if (hasTypeArgument()) {
290            if (!decl.isGeneric()) {
291              continue;
292            }
293            GenericMethodDecl genDecl = decl.genericDecl();
294            if (!(getNumTypeArgument() == genDecl.getNumTypeParameter())) {
295              continue;
296            }
297          }
298          applicable.add(decl);
299        }
300        return applicable;
301      }
302    
303      eq AmbiguousMethodReference.potentiallyApplicableMethods(FunctionDescriptor f) =
304          new ArrayList<MethodDecl>(); // TODO(jesper): use Collections.emptyList()
305    
306      // 15.13.1
307      syn lazy boolean MethodReference.isExact();
308      syn lazy MethodDecl MethodReference.exactCompileTimeDeclaration();
309    
310      eq MethodReference.isExact() = exactCompileTimeDeclaration() != unknownMethod();
311    
312      eq ExprMethodReference.exactCompileTimeDeclaration() {
313        Collection<MethodDecl> col = getExpr().type().memberMethods(name());
314        int foundCompatible = 0;
315        MethodDecl latestDecl = null;
316        for (MethodDecl decl  : col) {
317          if (decl.accessibleFrom(hostType())) {
318            foundCompatible++;
319            latestDecl = decl;
320          }
321        }
322        if (foundCompatible != 1) {
323          return unknownMethod();
324        }
325        if (latestDecl.isVariableArity()) {
326          return unknownMethod();
327        }
328        if (latestDecl.isGeneric()) {
329          GenericMethodDecl genericDecl = latestDecl.genericDecl();
330          if (getNumTypeArgument() == genericDecl.getNumTypeParameter()) {
331            return latestDecl;
332          } else {
333            return unknownMethod();
334          }
335        }
336        return latestDecl;
337      }
338    
339      eq TypeMethodReference.exactCompileTimeDeclaration() {
340        if (getTypeAccess().type().isRawType()) {
341          return unknownMethod();
342        }
343        Collection<MethodDecl> col = getTypeAccess().type().memberMethods(name());
344        int foundCompatible = 0;
345        MethodDecl latestDecl = null;
346        for (MethodDecl decl  : col) {
347          if (decl.accessibleFrom(hostType())) {
348            foundCompatible++;
349            latestDecl = decl;
350          }
351        }
352        if (foundCompatible != 1) {
353          return unknownMethod();
354        }
355        if (latestDecl.isVariableArity()) {
356          return unknownMethod();
357        }
358        if (latestDecl.isGeneric()) {
359          GenericMethodDecl genericDecl = latestDecl.genericDecl();
360          if (getNumTypeArgument() == genericDecl.getNumTypeParameter()) {
361            return latestDecl;
362          } else {
363            return unknownMethod();
364          }
365        }
366        return latestDecl;
367      }
368    
369      eq AmbiguousMethodReference.exactCompileTimeDeclaration() = unknownMethod();
370    }
371    
372    aspect Synthetics {
373      private TypeDecl SyntheticTypeAccess.type;
374      public SyntheticTypeAccess.SyntheticTypeAccess(TypeDecl type) {
375        this.type = type;
376      }
377      syn lazy TypeDecl SyntheticTypeAccess.type() = type;
378    
379      private FunctionDescriptor MethodReferenceAccess.targetDescriptor;
380      public MethodReferenceAccess.MethodReferenceAccess(String name,
381          List<Expr> args, FunctionDescriptor f) {
382        super(name, args);
383        this.targetDescriptor = f;
384      }
385    
386      private FunctionDescriptor ParMethodReferenceAccess.targetDescriptor;
387      public ParMethodReferenceAccess.ParMethodReferenceAccess(String name,
388          List<Expr> args, List<Access> typeArgs, FunctionDescriptor f) {
389        super(name, args, typeArgs);
390        this.targetDescriptor = f;
391      }
392    
393      private FunctionDescriptor ConstructorReferenceAccess.targetDescriptor;
394      public ConstructorReferenceAccess.ConstructorReferenceAccess(Access access,
395          List<Expr> args, FunctionDescriptor f) {
396        super(access, args);
397        this.targetDescriptor = f;
398      }
399    
400      private FunctionDescriptor ParConstructorReferenceAccess.targetDescriptor;
401      public ParConstructorReferenceAccess.ParConstructorReferenceAccess(Access access,
402          List<Expr> args, Opt<TypeDecl> optDecl, List<Access> typeArgs, FunctionDescriptor f) {
403        super(access, args, optDecl, typeArgs);
404        this.targetDescriptor = f;
405      }
406    }