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 }