1 /* 2 * Copyright (c) 1994, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.lang; 27 28 import sun.reflect.ReflectionFactory; 29 30 import java.lang.reflect.Method; 31 import java.lang.reflect.Modifier; 32 import java.security.AccessController; 33 import java.util.Arrays; 34 import java.util.Iterator; 35 import java.util.NoSuchElementException; 36 37 /** 38 * A table of methods with different operations for adding and removing methods. 39 * It implements {@link Iterable} which iterates {@link java.lang.reflect.Method} objects 40 * in order they were added to the table. 41 */ 42 interface MethodTable extends Iterable<Method> { 43 44 /** 45 * When requested {@code expectedMaxSize <= MAX_ARRAY_SIZE} then 46 * {@link java.lang.MethodTable.SimpleArrayImpl} is created 47 * else {@link HashArrayImpl} is created. 48 */ 49 int MAX_ARRAY_SIZE = 20; 50 51 /** 52 * Constructs new instance of MethodTable with enough capacity to 53 * hold {@code expectedMaxSize} methods. 54 * 55 * @param expectedMaxSize expected maximum size of MethodTable 56 * @return new instance of MethodTable 57 */ 58 static MethodTable newInstance(int expectedMaxSize) { 59 return expectedMaxSize <= MAX_ARRAY_SIZE 60 ? new SimpleArrayImpl(expectedMaxSize) 61 : new HashArrayImpl(expectedMaxSize); 62 } 63 64 /** 65 * Unconditionally adds given {@code method} to this MethodTable. 66 * 67 * @param method a method to add 68 */ 69 void add(Method method); 70 71 /** 72 * Adds given {@code method} to this MethodTable unless a method 73 * with same signature already exists and it's 74 * {@link java.lang.reflect.Method#getDeclaringClass() declaring class} 75 * is equal to given {@code declaringClass}. 76 * 77 * @param method a method to add 78 * @param declaringClass the class to check against declaring classes 79 * of existing methods with same signature 80 */ 81 void addUnlessDeclaredExists(Method method, Class<?> declaringClass); 82 83 /** 84 * Each existing method with same signature as given {@code method} 85 * is compared in turn with it and the following actions are taken: 86 * <ul> 87 * <li>if given method is same method as existing method, 88 * the operation completes without adding given method.</li> 89 * <li>if existing method is declared by given {@code declaringClass}, 90 * the operation completes without adding given method.</li> 91 * <li>if existing method is not abstract and either existing method 92 * overrides given method or existing method is declared by class and given 93 * method is declared by interface, the operation completes without 94 * adding given method.</li> 95 * <li>if given method is not abstract and either given method overrides 96 * existing method or given method is declared by class and existing 97 * method is declared by interface, then existing method is removed 98 * from this MethodTable.</li> 99 * <li>if there are no more existing methods then given method is added 100 * to this MethodTable and operation completes else next existing 101 * method is taken into consideration.</li> 102 * </ul> 103 * 104 * @param method a method to consolidate with existing methods of same 105 * signature 106 */ 107 void consolidate(Method method, Class<?> declaringClass); 108 109 /** 110 * @return the size of this MethodTable 111 */ 112 int getSize(); 113 114 /** 115 * Clears this MethodTable. 116 */ 117 void clear(); 118 119 /** 120 * @return new array of methods contained in this MethodTable 121 * in order they were added to this table. 122 */ 123 default Method[] getMethods() { 124 Method[] methods = new Method[getSize()]; 125 int i = 0; 126 for (Method m : this) { 127 methods[i++] = m; 128 } 129 return methods; 130 } 131 132 133 /** 134 * @return the 1st among the methods with the most specific return type 135 * if there is one or null if there are no methods in this MethodTable. 136 */ 137 default Method getFirstMethodWithMostSpecificReturnType() { 138 Method res = null; 139 for (Method m : this) { 140 if (res == null || ( 141 res.getReturnType() != m.getReturnType() && 142 res.getReturnType().isAssignableFrom(m.getReturnType()) 143 )) { 144 res = m; 145 } 146 } 147 return res; 148 } 149 150 /** 151 * A doubly-linked list of nodes. Each DLNode starts life as 152 * it's own doubly-linked singleton list.<p> 153 * {@link #unlink(Anchor anchor)} un-links a DLNode from a doubly-linked 154 * list with other nodes and liberates it so that it forms it's 155 * own singleton list. It also decrements {@code anchor}'s size.<p> 156 * {@link #linkBefore(Anchor anchor)} inserts a DLNode into the doubly-linked 157 * list before the {@code anchor} and increments it's size. 158 */ 159 interface DLNode { 160 DLNode getNextDln(); 161 162 void setNextDln(DLNode nextDln); 163 164 DLNode getPrevDln(); 165 166 void setPrevDln(DLNode prevDln); 167 168 /** 169 * Insert {@code this} DLNode before {@code anchor} 170 */ 171 default void linkBefore(Anchor anchor) { 172 // should be free before linking 173 assert this.getNextDln() == this && 174 this.getPrevDln() == this; 175 DLNode prev = anchor.getPrevDln(); 176 this.setNextDln(anchor); 177 anchor.setPrevDln(this); 178 prev.setNextDln(this); 179 this.setPrevDln(prev); 180 anchor.addToSize(1); 181 } 182 183 /** 184 * Remove {@code this} DLNode from doubly-linked list 185 */ 186 default void unlink(Anchor anchor) { 187 DLNode next = this.getNextDln(); 188 DLNode prev = this.getPrevDln(); 189 // should be linked before un-linking 190 assert next != this && prev != this; 191 next.setPrevDln(prev); 192 prev.setNextDln(next); 193 this.setNextDln(this); 194 this.setPrevDln(this); 195 anchor.addToSize(-1); 196 } 197 198 /** 199 * Anchor is a {@link DLNode} which keeps track 200 * of doubly-linked list size. 201 */ 202 interface Anchor extends DLNode { 203 void addToSize(int increment); 204 } 205 } 206 207 /** 208 * A node forming a linked-list of Method objects that share the same signature 209 * and a key with equals/hashCode that compares method signature. 210 * It also implements {@link DLNode} so it forms a separate doubly-linked list 211 * with other DLNode(s) to keep insertion order. 212 */ 213 final class MethodNode implements DLNode { 214 final Method method; 215 private final int hash; 216 MethodNode next; 217 218 MethodNode(Method method) { 219 this.method = method; 220 this.hash = method.getReturnType().hashCode() ^ 221 method.getName().hashCode() ^ 222 reflectionFactory.methodParameterTypesHashCode(method); 223 } 224 225 void add(MethodNode other, Anchor anchor) { 226 other.next = this.next; 227 this.next = other; 228 other.linkBefore(anchor); 229 } 230 231 void addUnlessDeclaredExists(MethodNode other, 232 Class<?> declaringClass, 233 Anchor anchor) { 234 for (MethodNode node = this; 235 node.method.getDeclaringClass() != declaringClass; 236 node = node.next 237 ) { 238 if (node.next == null) { 239 node.next = other; 240 other.linkBefore(anchor); 241 break; 242 } 243 } 244 } 245 246 MethodNode consolidate(MethodNode other, 247 Class<?> declaringClass, 248 Anchor anchor) { 249 Class<?> thisCl = this.method.getDeclaringClass(); 250 Class<?> otherCl = other.method.getDeclaringClass(); 251 252 if (thisCl == otherCl || // this.method is the same as other.method OR 253 thisCl == declaringClass) { // this.method is declared by declaringClass 254 // complete without adding other 255 return this; 256 } 257 258 boolean thisAbstract = Modifier.isAbstract(this.method.getModifiers()); 259 260 if (!thisAbstract && // this.method is non-abstract... 261 otherCl.isAssignableFrom(thisCl)) { // ...and overrides other.method 262 // complete without adding other 263 return this; 264 } 265 266 boolean thisInterface = thisCl.isInterface(); 267 boolean otherInterface = otherCl.isInterface(); 268 269 if (!thisAbstract && !thisInterface && // this.method is non-abstract class method... 270 otherInterface) { //...which wins over interface other.method 271 // complete without adding other 272 return this; 273 } 274 275 boolean otherAbstract = Modifier.isAbstract(other.method.getModifiers()); 276 277 if (!otherAbstract && // other.method is non-abstract... 278 thisCl.isAssignableFrom(otherCl)) { // ...and overrides this.method 279 // remove this and... 280 this.unlink(anchor); 281 if (next == null) { // ...add other 282 other.linkBefore(anchor); 283 return other; 284 } else { // ...continue with other 285 return next.consolidate(other, declaringClass, anchor); 286 } 287 } 288 289 if (!otherAbstract && !otherInterface && // other.method is non-abstract class method... 290 thisInterface) { //...which wins over interface this.method 291 // remove this and... 292 this.unlink(anchor); 293 if (next == null) { // ...add other 294 other.linkBefore(anchor); 295 return other; 296 } else { // ...continue with other 297 return next.consolidate(other, declaringClass, anchor); 298 } 299 } 300 301 // else keep this and... 302 if (next == null) { // ...add other 303 other.linkBefore(anchor); 304 next = other; 305 } else { //...continue with other 306 next = next.consolidate(other, declaringClass, anchor); 307 } 308 return this; 309 } 310 311 @Override 312 public int hashCode() { 313 return hash; 314 } 315 316 @Override 317 public boolean equals(Object obj) { 318 if (obj == this) return true; 319 if (!(obj instanceof MethodNode)) return false; 320 MethodNode other = (MethodNode) obj; 321 return this.method.getReturnType() == other.method.getReturnType() && 322 this.method.getName() == other.method.getName() && // Method.name is interned 323 reflectionFactory.methodParameterTypesEquals(this.method, other.method); 324 } 325 326 // we just need to compare method parameter types 327 328 private static final ReflectionFactory reflectionFactory 329 = AccessController.doPrivileged(new ReflectionFactory.GetReflectionFactoryAction()); 330 331 // DLNode implementation 332 333 private DLNode nextDln = this; 334 private DLNode prevDln = this; 335 336 @Override 337 public DLNode getNextDln() { 338 return nextDln; 339 } 340 341 @Override 342 public void setNextDln(DLNode nextDln) { 343 this.nextDln = nextDln; 344 } 345 346 @Override 347 public DLNode getPrevDln() { 348 return prevDln; 349 } 350 351 @Override 352 public void setPrevDln(DLNode prevDln) { 353 this.prevDln = prevDln; 354 } 355 } 356 357 /** 358 * MethodTable implementation based on {@link HashArray}. 359 * It also implements {@link DLNode.Anchor} to 360 * form a doubly-linked list with other DLNode(s). 361 */ 362 final class HashArrayImpl extends HashArray<MethodNode> implements MethodTable, DLNode.Anchor { 363 364 HashArrayImpl(int expectedMaxSize) { 365 super(expectedMaxSize); 366 } 367 368 @Override 369 public void add(Method method) { 370 MethodNode n = new MethodNode(method); 371 MethodNode n0 = putIfAbsent(n); 372 if (n0 == null) { 373 n.linkBefore(this); 374 } else { 375 n0.add(n, this); 376 } 377 } 378 379 @Override 380 public void addUnlessDeclaredExists(Method method, Class<?> declaringClass) { 381 MethodNode n = new MethodNode(method); 382 MethodNode n0 = putIfAbsent(n); 383 if (n0 == null) { 384 n.linkBefore(this); 385 } else { 386 n0.addUnlessDeclaredExists(n, declaringClass, this); 387 } 388 } 389 390 @Override 391 public void consolidate(Method method, Class<?> declaringClass) { 392 MethodNode n = new MethodNode(method); 393 MethodNode n0 = putIfAbsent(n); 394 if (n0 == null) { 395 n.linkBefore(this); 396 } else { 397 MethodNode n1 = n0.consolidate(n, declaringClass, this); 398 if (n1 != n0) { 399 put(n1); 400 } 401 } 402 } 403 404 @Override 405 public int getSize() { 406 return size; 407 } 408 409 @Override 410 public void clear() { 411 super.clear(); 412 nextDln = prevDln = this; 413 size = 0; 414 } 415 416 // Iterable<Method> implementation 417 418 @Override 419 public Iterator<Method> iterator() { 420 return new Iterator<Method>() { 421 422 DLNode dln = HashArrayImpl.this.getNextDln(); 423 424 @Override 425 public boolean hasNext() { 426 return dln != HashArrayImpl.this; 427 } 428 429 @Override 430 public Method next() { 431 if (!hasNext()) { 432 throw new NoSuchElementException(); 433 } 434 Method result = ((MethodNode) dln).method; 435 dln = dln.getNextDln(); 436 return result; 437 } 438 }; 439 } 440 441 // DLNode.Anchor implementation 442 443 private DLNode nextDln = this; 444 private DLNode prevDln = this; 445 private int size; 446 447 @Override 448 public DLNode getNextDln() { 449 return nextDln; 450 } 451 452 @Override 453 public void setNextDln(DLNode nextDln) { 454 this.nextDln = nextDln; 455 } 456 457 @Override 458 public DLNode getPrevDln() { 459 return prevDln; 460 } 461 462 @Override 463 public void setPrevDln(DLNode prevDln) { 464 this.prevDln = prevDln; 465 } 466 467 @Override 468 public void addToSize(int increment) { 469 size += increment; 470 } 471 } 472 473 /** 474 * Simple array based MethodTable implementation for small number of methods. 475 */ 476 final class SimpleArrayImpl implements MethodTable { 477 478 private final Method[] methods; 479 private int hwm; // High Water Mark 480 481 SimpleArrayImpl(int expectedMaxSize) { 482 methods = new Method[expectedMaxSize]; 483 } 484 485 @Override 486 public void add(Method method) { 487 methods[hwm++] = method; 488 } 489 490 @Override 491 public void addUnlessDeclaredExists(Method otherM, Class<?> declaringClass) { 492 for (int i = 0; i < hwm; i++) { 493 Method thisM = methods[i]; 494 if (thisM == null || // mind the gap 495 thisM.getReturnType() != otherM.getReturnType() || // not same signature 496 thisM.getName() != otherM.getName() || // Method.name is interned 497 !MethodNode.reflectionFactory.methodParameterTypesEquals(thisM, otherM)) { 498 continue; 499 } 500 // thisM has same signature as otherM 501 502 if (thisM.getDeclaringClass() == declaringClass) { 503 return; 504 } 505 // continue with next thisM 506 } 507 // add otherM 508 methods[hwm++] = otherM; 509 } 510 511 @Override 512 public void consolidate(Method otherM, Class<?> declaringClass) { 513 for (int i = 0; i < hwm; i++) { 514 Method thisM = methods[i]; 515 if (thisM == null || // mind the gap 516 thisM.getReturnType() != otherM.getReturnType() || // not same signature 517 thisM.getName() != otherM.getName() || // Method.name is interned 518 !MethodNode.reflectionFactory.methodParameterTypesEquals(thisM, otherM)) { 519 continue; 520 } 521 // thisM has same signature as otherM 522 523 Class<?> thisCl = thisM.getDeclaringClass(); 524 Class<?> otherCl = otherM.getDeclaringClass(); 525 526 if (thisCl == otherCl || // thisM is the same as otherM OR 527 thisCl == declaringClass) { // thisM is declared by declaringClass 528 // complete without adding otherM 529 return; 530 } 531 532 boolean thisAbstract = Modifier.isAbstract(thisM.getModifiers()); 533 534 if (!thisAbstract && // thisM is non-abstract... 535 otherCl.isAssignableFrom(thisCl)) { // ...and overrides otherM 536 // complete without adding otherM 537 return; 538 } 539 540 boolean thisInterface = thisCl.isInterface(); 541 boolean otherInterface = otherCl.isInterface(); 542 543 if (!thisAbstract && !thisInterface && // this.method is non-abstract class method... 544 otherInterface) { //...which wins over interface other.method 545 // complete without adding otherM 546 return; 547 } 548 549 boolean otherAbstract = Modifier.isAbstract(otherM.getModifiers()); 550 551 if (!otherAbstract && // other.method is non-abstract... 552 thisCl.isAssignableFrom(otherCl)) { // ...and overrides this.method 553 // remove this 554 methods[i] = null; 555 continue; 556 } 557 558 if (!otherAbstract && !otherInterface && // other.method is non-abstract class method... 559 thisInterface) { //...which wins over interface this.method 560 // remove this 561 methods[i] = null; 562 } 563 // continue with next thisM 564 } 565 // add otherM 566 methods[hwm++] = otherM; 567 } 568 569 @Override 570 public int getSize() { 571 int size = 0; 572 for (int i = 0; i < hwm; i++) { 573 if (methods[i] != null) { 574 size++; 575 } 576 } 577 return size; 578 } 579 580 @Override 581 public void clear() { 582 Arrays.fill(methods, null); 583 hwm = 0; 584 } 585 586 @Override 587 public Iterator<Method> iterator() { 588 return new Iterator<Method>() { 589 int i = 0; 590 Method next; 591 592 private Method nextMethod() { 593 while (next == null && i < hwm) next = methods[i++]; 594 return next; 595 } 596 597 @Override 598 public boolean hasNext() { 599 return nextMethod() != null; 600 } 601 602 @Override 603 public Method next() { 604 Method m = nextMethod(); 605 if (m == null) throw new NoSuchElementException(); 606 next = null; 607 return m; 608 } 609 }; 610 } 611 } 612 }