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 }