1 52 53 package com.go.trove.util; 54 55 import java.lang.reflect.*; 56 import java.util.*; 57 import java.io.OutputStream ; 58 import java.io.IOException ; 59 import com.go.trove.classfile.*; 60 61 79 public class MergedClass { 80 private static Map cMergedMap; 86 87 static { 88 try { 89 cMergedMap = new IdentityMap(7); 90 } 91 catch (LinkageError e) { 92 cMergedMap = new HashMap(7); 93 } 94 catch (Exception e) { 95 cMergedMap = new HashMap(7); 100 } 101 } 102 103 124 public static Constructor getConstructor(ClassInjector injector, 125 Class [] classes) 126 throws IllegalArgumentException  127 { 128 return getConstructor(injector, classes, null); 129 } 130 131 165 public static Constructor getConstructor(ClassInjector injector, 166 Class [] classes, 167 String [] prefixes) 168 throws IllegalArgumentException  169 { 170 if (classes.length > 254) { 171 throw new IllegalArgumentException  172 ("More than 254 merged classes: " + classes.length); 173 } 174 175 Class clazz = getMergedClass(injector, classes, prefixes); 176 177 try { 178 return clazz.getConstructor(classes); 179 } 180 catch (NoSuchMethodException e) { 181 throw new InternalError (e.toString()); 182 } 183 } 184 185 203 public static Constructor getConstructor2(ClassInjector injector, 204 Class [] classes) 205 throws IllegalArgumentException  206 { 207 return getConstructor2(injector, classes, null); 208 } 209 210 241 public static Constructor getConstructor2(ClassInjector injector, 242 Class [] classes, 243 String [] prefixes) 244 throws IllegalArgumentException  245 { 246 Class clazz = getMergedClass(injector, classes, prefixes); 247 248 try { 249 return clazz.getConstructor(new Class []{InstanceFactory.class}); 250 } 251 catch (NoSuchMethodException e) { 252 throw new InternalError (e.toString()); 253 } 254 } 255 256 private static Class getMergedClass(ClassInjector injector, 257 Class [] classes, 258 String [] prefixes) 259 throws IllegalArgumentException  260 { 261 ClassEntry[] classEntries = new ClassEntry[classes.length]; 262 for (int i=0; i<classes.length; i++) { 263 try { 266 classes[i] = injector.loadClass(classes[i].getName()); 267 } 268 catch (ClassNotFoundException e) { 269 throw new IllegalArgumentException  270 ("Unable to load class from injector: " + classes[i]); 271 } 272 273 if (prefixes == null || i >= prefixes.length) { 274 classEntries[i] = new ClassEntry(classes[i]); 275 } 276 else { 277 String prefix = prefixes[i]; 278 if (prefix != null && prefix.length() == 0) { 279 prefix = null; 280 } 281 classEntries[i] = new ClassEntry(classes[i], prefix); 282 } 283 } 284 285 return getMergedClass(injector, classEntries); 286 } 287 288 293 private static synchronized Class getMergedClass(ClassInjector injector, 294 ClassEntry[] classEntries) 295 throws IllegalArgumentException  296 { 297 Map classListMap = (Map)cMergedMap.get(injector); 298 if (classListMap == null) { 299 classListMap = new HashMap(7); 300 cMergedMap.put(injector, classListMap); 301 } 302 303 Object key = generateKey(classEntries); 304 String mergedName = (String )classListMap.get(key); 305 if (mergedName != null) { 306 try { 307 return injector.loadClass(mergedName); 308 } 309 catch (ClassNotFoundException e) { 310 } 311 } 312 313 ClassFile cf; 314 try { 315 cf = createClassFile(injector, classEntries); 316 } 317 catch (IllegalArgumentException e) { 318 e.fillInStackTrace(); 319 throw e; 320 } 321 322 333 334 try { 335 OutputStream stream = injector.getStream(cf.getClassName()); 336 cf.writeTo(stream); 337 stream.close(); 338 } 339 catch (IOException e) { 340 throw new InternalError (e.toString()); 341 } 342 343 Class merged; 344 try { 345 merged = injector.loadClass(cf.getClassName()); 346 } 347 catch (ClassNotFoundException e) { 348 throw new InternalError (e.toString()); 349 } 350 351 classListMap.put(key, merged.getName()); 352 return merged; 353 } 354 355 private static Object generateKey(ClassEntry[] classEntries) { 356 int length = classEntries.length; 357 Object [] mainElements = new Object [length]; 358 for (int i=0; i<length; i++) { 359 ClassEntry classEntry = classEntries[i]; 360 mainElements[i] = new MultiKey(new Object [] { 361 classEntry.getClazz().getName(), 362 classEntry.getMethodPrefix() 363 }); 364 } 365 return new MultiKey(mainElements); 366 } 367 368 private static ClassFile createClassFile(ClassInjector injector, 369 ClassEntry[] classEntries) 370 throws IllegalArgumentException  371 { 372 Set classSet = new HashSet(classEntries.length * 2 + 1); 373 Set nonConflictingClasses = new HashSet(classEntries.length * 2 + 1); 374 String commonPackage = null; 375 Map methodMap = new HashMap(); 376 377 for (int i=0; i<classEntries.length; i++) { 378 ClassEntry classEntry = classEntries[i]; 379 Class clazz = classEntry.getClazz(); 380 381 if (clazz.isPrimitive()) { 382 throw new IllegalArgumentException  383 ("Merged classes cannot be primitive: " + clazz); 384 } 385 386 if (!classSet.add(classEntry)) { 387 throw new IllegalArgumentException  388 ("Class is specified more than once: " + clazz); 389 } 390 391 if (!Modifier.isPublic(clazz.getModifiers())) { 392 String classPackage = clazz.getName(); 393 int index = classPackage.lastIndexOf('.'); 394 if (index < 0) { 395 classPackage = ""; 396 } 397 else { 398 classPackage = classPackage.substring(0, index); 399 } 400 401 if (commonPackage == null) { 402 commonPackage = classPackage; 403 } 404 else if (!commonPackage.equals(classPackage)) { 405 throw new IllegalArgumentException  406 ("Not all non-public classes defined in same " + 407 "package: " + commonPackage); 408 } 409 } 410 411 nonConflictingClasses.add(classEntry); 413 414 Method[] methods = clazz.getMethods(); 415 String prefix = classEntry.getMethodPrefix(); 416 417 for (int j=0; j<methods.length; j++) { 418 Method method = methods[j]; 419 String name = method.getName(); 420 if ("<clinit>".equals(name)) { 422 continue; 423 } 424 425 MethodEntry methodEntry = new MethodEntry(method, name); 426 MethodEntry existing = (MethodEntry)methodMap.get(methodEntry); 427 428 if (existing == null) { 429 methodMap.put(methodEntry, methodEntry); 430 } 431 else if (existing.returnTypeDiffers(methodEntry)) { 432 nonConflictingClasses.remove(classEntry); 433 if (prefix == null) { 434 throw new IllegalArgumentException  435 ("Conflicting return types: " + 436 existing + ", " + methodEntry); 437 } 438 } 439 440 if (prefix != null) { 441 name = prefix + name; 442 443 methodEntry = new MethodEntry(method, name); 444 existing = (MethodEntry)methodMap.get(methodEntry); 445 446 if (existing == null) { 447 methodMap.put(methodEntry, methodEntry); 448 } 449 else if (existing.returnTypeDiffers(methodEntry)) { 450 nonConflictingClasses.remove(classEntry); 451 throw new IllegalArgumentException  452 ("Conflicting return types: " + 453 existing + ", " + methodEntry); 454 } 455 } 456 } 457 } 458 459 int id = 0; 460 Iterator it = classSet.iterator(); 461 while (it.hasNext()) { 462 id = id * 31 + it.next().hashCode(); 463 } 464 465 String className = "MergedClass$"; 466 try { 467 while (true) { 468 className = "MergedClass$" + (id & 0xffffffffL); 469 if (commonPackage != null && commonPackage.length() > 0) { 470 className = commonPackage + '.' + className; 471 } 472 try { 473 injector.loadClass(className); 474 } 475 catch (LinkageError e) { 476 } 477 id++; 478 } 479 } 480 catch (ClassNotFoundException e) { 481 } 482 483 ClassFile cf = new ClassFile(className); 484 cf.getAccessFlags().setFinal(true); 485 cf.markSynthetic(); 486 487 for (int i=0; i<classEntries.length; i++) { 488 ClassEntry classEntry = classEntries[i]; 489 if (nonConflictingClasses.contains(classEntry)) { 490 addAllInterfaces(cf, classEntry.getClazz()); 491 } 492 } 493 494 AccessFlags privateAccess = new AccessFlags(); 495 privateAccess.setPrivate(true); 496 AccessFlags privateFinalAccess = new AccessFlags(); 497 privateFinalAccess.setPrivate(true); 498 privateFinalAccess.setFinal(true); 499 AccessFlags publicAccess = new AccessFlags(); 500 publicAccess.setPublic(true); 501 502 TypeDescriptor instanceFactoryType = 504 new TypeDescriptor(InstanceFactory.class); 505 cf.addField(privateAccess, 506 "instanceFactory", instanceFactoryType).markSynthetic(); 507 508 Method instanceFactoryMethod; 509 try { 510 instanceFactoryMethod = 511 InstanceFactory.class.getMethod 512 ("getInstance", new Class []{int.class}); 513 } 514 catch (NoSuchMethodException e) { 515 throw new InternalError (e.toString()); 516 } 517 518 String [] fieldNames = new String [classEntries.length]; 521 TypeDescriptor[] types = new TypeDescriptor[classEntries.length]; 522 for (int i=0; i<classEntries.length; i++) { 523 Class clazz = classEntries[i].getClazz(); 524 String fieldName = "m$" + i; 525 TypeDescriptor type = new TypeDescriptor(clazz); 526 cf.addField(privateAccess, fieldName, type).markSynthetic(); 527 fieldNames[i] = fieldName; 528 types[i] = type; 529 530 MethodInfo mi = cf.addMethod 533 (privateFinalAccess, fieldName, type, null); 534 mi.markSynthetic(); 535 CodeBuilder builder = new CodeBuilder(mi); 536 builder.loadThis(); 537 builder.loadField(fieldName, type); 538 builder.dup(); 539 Label isNull = builder.createLabel(); 540 builder.ifNullBranch(isNull, true); 541 builder.returnValue(Object .class); 543 isNull.setLocation(); 544 builder.pop(); 546 builder.loadThis(); 547 builder.loadField("instanceFactory", instanceFactoryType); 548 builder.dup(); 549 Label haveInstanceFactory = builder.createLabel(); 550 builder.ifNullBranch(haveInstanceFactory, false); 551 builder.loadConstant(null); 553 builder.returnValue(Object .class); 554 haveInstanceFactory.setLocation(); 555 builder.loadConstant(i); 556 builder.invoke(instanceFactoryMethod); 557 builder.checkCast(type); 558 builder.dup(); 559 builder.loadThis(); 560 builder.swap(); 561 builder.storeField(fieldName, type); 562 builder.returnValue(Object .class); 563 } 564 565 if (classEntries.length <= 254) { 567 MethodInfo mi = cf.addConstructor(publicAccess, types); 568 569 CodeBuilder builder = new CodeBuilder(mi); 570 builder.loadThis(); 571 builder.invokeSuperConstructor(null); 572 LocalVariable[] params = builder.getParameters(); 573 574 for (int i=0; i<classEntries.length; i++) { 575 builder.loadThis(); 576 builder.loadLocal(params[i]); 577 builder.storeField(fieldNames[i], types[i]); 578 } 579 580 builder.returnVoid(); 581 builder = null; 582 } 583 584 MethodInfo mi = cf.addConstructor 586 (publicAccess, new TypeDescriptor[]{instanceFactoryType}); 587 588 CodeBuilder builder = new CodeBuilder(mi); 589 builder.loadThis(); 590 builder.invokeSuperConstructor(null); 591 builder.loadThis(); 592 builder.loadLocal(builder.getParameters()[0]); 593 builder.storeField("instanceFactory", instanceFactoryType); 594 595 builder.returnVoid(); 596 builder = null; 597 598 Set methodSet = methodMap.keySet(); 599 600 for (int i=0; i<classEntries.length; i++) { 602 ClassEntry classEntry = classEntries[i]; 603 String prefix = classEntry.getMethodPrefix(); 604 String fieldName = fieldNames[i]; 605 TypeDescriptor type = types[i]; 606 607 Method[] methods = classEntry.getClazz().getMethods(); 608 for (int j=0; j<methods.length; j++) { 609 Method method = methods[j]; 610 if ("<clinit>".equals(method.getName())) { 612 continue; 613 } 614 615 MethodEntry methodEntry = new MethodEntry(method); 616 if (methodSet.contains(methodEntry)) { 617 methodSet.remove(methodEntry); 618 addWrapperMethod(cf, methodEntry, fieldName, type); 619 } 620 621 if (prefix != null) { 622 methodEntry = new MethodEntry 623 (method, prefix + method.getName()); 624 if (methodSet.contains(methodEntry)) { 625 methodSet.remove(methodEntry); 626 addWrapperMethod(cf, methodEntry, fieldName, type); 627 } 628 } 629 } 630 } 631 632 return cf; 633 } 634 635 private static void addAllInterfaces(ClassFile cf, Class clazz) { 636 if (clazz == null) { 637 return; 638 } 639 640 if (clazz.isInterface()) { 641 cf.addInterface(clazz); 642 } 643 644 addAllInterfaces(cf, clazz.getSuperclass()); 645 646 Class [] interfaces = clazz.getInterfaces(); 647 for (int i=0; i<interfaces.length; i++) { 648 addAllInterfaces(cf, interfaces[i]); 649 } 650 } 651 652 private static void addWrapperMethod(ClassFile cf, 653 MethodEntry methodEntry, 654 String fieldName, 655 TypeDescriptor type) { 656 Method method = methodEntry.getMethod(); 657 658 if (isDefinedInObject(method)) { 660 return; 661 } 662 663 AccessFlags flags = new AccessFlags(method.getModifiers()); 664 flags.setAbstract(false); 665 flags.setFinal(true); 666 flags.setSynchronized(false); 667 flags.setNative(false); 668 flags.setStatic(false); 669 670 AccessFlags staticFlags = (AccessFlags)flags.clone(); 671 staticFlags.setStatic(true); 672 673 TypeDescriptor ret = new TypeDescriptor(method.getReturnType()); 674 675 Class [] paramClasses = method.getParameterTypes(); 676 TypeDescriptor[] params = new TypeDescriptor[paramClasses.length]; 677 for (int i=0; i<params.length; i++) { 678 params[i] = new TypeDescriptor(paramClasses[i]); 679 } 680 681 MethodInfo mi; 682 if (Modifier.isStatic(method.getModifiers())) { 683 mi = cf.addMethod 684 (staticFlags, methodEntry.getName(), ret, params); 685 } 686 else { 687 mi = cf.addMethod(flags, methodEntry.getName(), ret, params); 688 } 689 690 Class [] exceptions = method.getExceptionTypes(); 692 for (int i=0; i<exceptions.length; i++) { 693 mi.addException(exceptions[i].getName()); 694 } 695 696 CodeBuilder builder = new CodeBuilder(mi); 698 699 if (!Modifier.isStatic(method.getModifiers())) { 700 builder.loadThis(); 701 builder.loadField(fieldName, type); 702 Label isNonNull = builder.createLabel(); 703 builder.dup(); 704 builder.ifNullBranch(isNonNull, false); 705 builder.pop(); 707 builder.loadThis(); 710 builder.invokePrivate(fieldName, type, null); 711 isNonNull.setLocation(); 712 } 713 LocalVariable[] locals = builder.getParameters(); 714 for (int i=0; i<locals.length; i++) { 715 builder.loadLocal(locals[i]); 716 } 717 builder.invoke(method); 718 719 if (method.getReturnType() == void.class) { 720 builder.returnVoid(); 721 } 722 else { 723 builder.returnValue(method.getReturnType()); 724 } 725 } 726 727 private static boolean isDefinedInObject(Method method) { 728 if (method.getDeclaringClass() == Object .class) { 729 return true; 730 } 731 732 Class [] types = method.getParameterTypes(); 733 String name = method.getName(); 734 735 if (types.length == 0) { 736 return 737 "hashCode".equals(name) || 738 "clone".equals(name) || 739 "toString".equals(name) || 740 "finalize".equals(name); 741 } 742 else { 743 return 744 types.length == 1 && 745 types[0] == Object .class && 746 "equals".equals(name); 747 } 748 } 749 750 private MergedClass() { 751 } 752 753 757 public interface InstanceFactory { 758 762 public Object getInstance(int index); 763 } 764 765 private static class ClassEntry { 766 private final Class mClazz; 767 private final String mPrefix; 768 769 public ClassEntry(Class clazz) { 770 this(clazz, null); 771 } 772 773 public ClassEntry(Class clazz, String prefix) { 774 mClazz = clazz; 775 mPrefix = prefix; 776 } 777 778 public Class getClazz() { 779 return mClazz; 780 } 781 782 public String getMethodPrefix() { 783 return mPrefix; 784 } 785 786 public int hashCode() { 787 int hash = mClazz.getName().hashCode(); 788 return (mPrefix == null) ? hash : hash ^ mPrefix.hashCode(); 789 } 790 791 public boolean equals(Object other) { 792 if (other instanceof ClassEntry) { 793 ClassEntry classEntry = (ClassEntry)other; 794 if (mClazz == classEntry.mClazz) { 795 if (mPrefix == null) { 796 return classEntry.mPrefix == null; 797 } 798 else { 799 return mPrefix.equals(classEntry.mPrefix); 800 } 801 } 802 } 803 return false; 804 } 805 806 public String toString() { 807 return mClazz.toString(); 808 } 809 } 810 811 private static class MethodEntry { 812 private final Method mMethod; 813 private final String mName; 814 private List mParams; 815 private int mHashCode; 816 817 public MethodEntry(Method method) { 818 this(method, method.getName()); 819 } 820 821 public MethodEntry(Method method, String name) { 822 mMethod = method; 823 mName = name; 824 mParams = Arrays.asList(method.getParameterTypes()); 825 mHashCode = mName.hashCode() ^ mParams.hashCode(); 826 } 827 828 public Method getMethod() { 829 return mMethod; 830 } 831 832 public String getName() { 833 return mName; 834 } 835 836 public boolean returnTypeDiffers(MethodEntry methodEntry) { 837 return getMethod().getReturnType() != 838 methodEntry.getMethod().getReturnType(); 839 } 840 841 public int hashCode() { 842 return mHashCode; 843 } 844 845 public boolean equals(Object other) { 846 if (!(other instanceof MethodEntry)) { 847 return false; 848 } 849 MethodEntry methodEntry = (MethodEntry)other; 850 return mName.equals(methodEntry.mName) && 851 mParams.equals(methodEntry.mParams); 852 } 853 854 public String toString() { 855 return mMethod.toString(); 856 } 857 } 858 } 859
| Popular Tags
|