Fix for JRUBY-2882. Handle error messages related to constructors better
[jruby.git] / src / org / jruby / javasupport / proxy / JavaProxyClassFactory.java
blob40cecca668da91bad3bd1266aecfa04f0b8dc40a
1 /***** BEGIN LICENSE BLOCK *****
2 * Version: CPL 1.0/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Common Public
5 * License Version 1.0 (the "License"); you may not use this file
6 * except in compliance with the License. You may obtain a copy of
7 * the License at http://www.eclipse.org/legal/cpl-v10.html
9 * Software distributed under the License is distributed on an "AS
10 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11 * implied. See the License for the specific language governing
12 * rights and limitations under the License.
14 * Copyright (C) 2006 Kresten Krab Thorup <krab@gnu.org>
16 * Alternatively, the contents of this file may be used under the terms of
17 * either of the GNU General Public License Version 2 or later (the "GPL"),
18 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
19 * in which case the provisions of the GPL or the LGPL are applicable instead
20 * of those above. If you wish to allow use of your version of this file only
21 * under the terms of either the GPL or the LGPL, and not to allow others to
22 * use your version of this file under the terms of the CPL, indicate your
23 * decision by deleting the provisions above and replace them with the notice
24 * and other provisions required by the GPL or the LGPL. If you do not delete
25 * the provisions above, a recipient may use your version of this file under
26 * the terms of any one of the CPL, the GPL or the LGPL.
27 ***** END LICENSE BLOCK *****/
29 package org.jruby.javasupport.proxy;
31 import java.lang.reflect.Constructor;
32 import java.lang.reflect.Field;
33 import java.lang.reflect.InvocationTargetException;
34 import java.lang.reflect.Method;
35 import java.lang.reflect.Modifier;
36 import java.lang.reflect.UndeclaredThrowableException;
37 import java.security.AccessController;
38 import java.security.PrivilegedAction;
39 import java.security.ProtectionDomain;
40 import java.util.Arrays;
41 import java.util.Collections;
42 import java.util.HashMap;
43 import java.util.HashSet;
44 import java.util.Iterator;
45 import java.util.Map;
46 import java.util.Set;
48 import org.jruby.Ruby;
49 import org.objectweb.asm.ClassVisitor;
50 import org.objectweb.asm.ClassWriter;
51 import org.objectweb.asm.FieldVisitor;
52 import org.objectweb.asm.Label;
53 import org.objectweb.asm.Opcodes;
54 import org.objectweb.asm.Type;
55 import org.objectweb.asm.commons.GeneratorAdapter;
57 public class JavaProxyClassFactory {
59 private static final Type JAVA_LANG_CLASS_TYPE = Type.getType(Class.class);
61 private static final Type[] EMPTY_TYPE_ARR = new Type[0];
63 private static final org.objectweb.asm.commons.Method HELPER_GET_PROXY_CLASS_METHOD = org.objectweb.asm.commons.Method
64 .getMethod(JavaProxyClass.class.getName()
65 + " initProxyClass(java.lang.Class)");
67 private static final org.objectweb.asm.commons.Method CLASS_FORNAME_METHOD = org.objectweb.asm.commons.Method
68 .getMethod("java.lang.Class forName(java.lang.String)");
70 private static final String INVOCATION_HANDLER_FIELD_NAME = "__handler";
72 private static final String PROXY_CLASS_FIELD_NAME = "__proxy_class";
74 private static final Class[] EMPTY_CLASS_ARR = new Class[0];
76 private static final Type INVOCATION_HANDLER_TYPE = Type
77 .getType(JavaProxyInvocationHandler.class);
79 private static final Type PROXY_METHOD_TYPE = Type
80 .getType(JavaProxyMethod.class);
82 private static final Type PROXY_CLASS_TYPE = Type
83 .getType(JavaProxyClass.class);
85 private static final org.objectweb.asm.commons.Method INVOCATION_HANDLER_INVOKE_METHOD = org.objectweb.asm.commons.Method
86 .getMethod("java.lang.Object invoke(java.lang.Object, "
87 + PROXY_METHOD_TYPE.getClassName()
88 + ", java.lang.Object[])");
90 private static final Type PROXY_HELPER_TYPE = Type
91 .getType(InternalJavaProxyHelper.class);
93 private static final org.objectweb.asm.commons.Method PROXY_HELPER_GET_METHOD = org.objectweb.asm.commons.Method
94 .getMethod(PROXY_METHOD_TYPE.getClassName() + " initProxyMethod("
95 + JavaProxyClass.class.getName()
96 + ",java.lang.String,java.lang.String,boolean)");
98 private static final Type JAVA_PROXY_TYPE = Type
99 .getType(InternalJavaProxy.class);
101 private static int counter;
103 private static Map proxies = Collections.synchronizedMap(new HashMap());
105 private static Method defineClass_method; // statically initialized below
107 private static synchronized int nextId() {
108 return counter++;
111 @Deprecated
112 static JavaProxyClass newProxyClass(ClassLoader loader,
113 String targetClassName, Class superClass, Class[] interfaces, Set names)
114 throws InvocationTargetException {
115 return newProxyClass(JavaProxyClass.runtimeTLS.get(), loader, targetClassName, superClass, interfaces, names);
118 // TODO: we should be able to optimize this quite a bit post-1.0. JavaClass already
119 // has all the methods organized by method name; the next version (supporting protected
120 // methods/fields) will have them organized even further. So collectMethods here can
121 // just lookup the overridden methods in the JavaClass map, should be much faster.
122 static JavaProxyClass newProxyClass(Ruby runtime, ClassLoader loader,
123 String targetClassName, Class superClass, Class[] interfaces, Set names)
124 throws InvocationTargetException {
125 if (loader == null) {
126 loader = JavaProxyClassFactory.class.getClassLoader();
129 if (superClass == null) {
130 superClass = Object.class;
133 if (interfaces == null) {
134 interfaces = EMPTY_CLASS_ARR;
137 Set key = new HashSet();
138 key.add(superClass);
139 for (int i = 0; i < interfaces.length; i++) {
140 key.add(interfaces[i]);
143 // add (potentially) overridden names to the key.
144 // TODO: see note above re: optimizations
145 if (names != null) {
146 key.addAll(names);
149 JavaProxyClass proxyClass = (JavaProxyClass) proxies.get(key);
150 if (proxyClass == null) {
152 if (targetClassName == null) {
153 // We always prepend an org.jruby.proxy package to the beginning
154 // because java and javax packages are protected and signed
155 // jars prevent us generating new classes with those package
156 // names. See JRUBY-2439.
157 String pkg = "org.jruby.proxy." + packageName(superClass);
158 String fullName = superClass.getName();
159 int ix = fullName.lastIndexOf('.');
160 String cName = fullName;
161 if(ix != -1) {
162 cName = fullName.substring(ix+1);
164 targetClassName = pkg + "." + cName + "$Proxy" + nextId();
167 validateArgs(runtime, targetClassName, superClass);
169 Map methods = new HashMap();
170 collectMethods(superClass, interfaces, methods, names);
172 Type selfType = Type.getType("L"
173 + toInternalClassName(targetClassName) + ";");
174 proxyClass = generate(loader, targetClassName, superClass,
175 interfaces, methods, selfType);
177 proxies.put(key, proxyClass);
180 return proxyClass;
183 static JavaProxyClass newProxyClass(ClassLoader loader,
184 String targetClassName, Class superClass, Class[] interfaces)
185 throws InvocationTargetException {
186 return newProxyClass(loader,targetClassName,superClass,interfaces,null);
189 private static JavaProxyClass generate(final ClassLoader loader,
190 final String targetClassName, final Class superClass,
191 final Class[] interfaces, final Map methods, final Type selfType) {
192 ClassWriter cw = beginProxyClass(targetClassName, superClass,
193 interfaces);
195 GeneratorAdapter clazzInit = createClassInitializer(selfType, cw);
197 generateConstructors(superClass, selfType, cw);
199 generateGetProxyClass(selfType, cw);
201 generateGetInvocationHandler(selfType, cw);
203 generateProxyMethods(superClass, methods, selfType, cw, clazzInit);
205 // finish class initializer
206 clazzInit.returnValue();
207 clazzInit.endMethod();
209 // end class
210 cw.visitEnd();
212 byte[] data = cw.toByteArray();
215 * try { FileOutputStream o = new
216 * FileOutputStream(targetClassName.replace( '/', '.') + ".class");
217 * o.write(data); o.close(); } catch (IOException ex) {
218 * ex.printStackTrace(); }
221 Class clazz = invokeDefineClass(loader, selfType.getClassName(), data);
223 // trigger class initialization for the class
224 try {
225 Field proxy_class = clazz.getDeclaredField(PROXY_CLASS_FIELD_NAME);
226 proxy_class.setAccessible(true);
227 return (JavaProxyClass) proxy_class.get(clazz);
228 } catch (Exception ex) {
229 InternalError ie = new InternalError();
230 ie.initCause(ex);
231 throw ie;
235 static {
236 AccessController.doPrivileged(new PrivilegedAction() {
237 public Object run() {
238 try {
239 defineClass_method = ClassLoader.class.getDeclaredMethod(
240 "defineClass", new Class[] { String.class,
241 byte[].class, int.class, int.class, ProtectionDomain.class });
242 } catch (Exception e) {
243 // should not happen!
244 e.printStackTrace();
245 return null;
247 defineClass_method.setAccessible(true);
248 return null;
253 private static Class invokeDefineClass(ClassLoader loader,
254 String className, byte[] data) {
255 try {
256 return (Class) defineClass_method
257 .invoke(loader, new Object[] { className, data,
258 new Integer(0), new Integer(data.length), JavaProxyClassFactory.class.getProtectionDomain() });
259 } catch (IllegalArgumentException e) {
260 // TODO Auto-generated catch block
261 e.printStackTrace();
262 return null;
263 } catch (IllegalAccessException e) {
264 // TODO Auto-generated catch block
265 e.printStackTrace();
266 return null;
267 } catch (InvocationTargetException e) {
268 // TODO Auto-generated catch block
269 e.printStackTrace();
270 return null;
274 private static ClassWriter beginProxyClass(final String targetClassName,
275 final Class superClass, final Class[] interfaces) {
277 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
279 int access = Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_STATIC;
280 String name = toInternalClassName(targetClassName);
281 String signature = null;
282 String supername = toInternalClassName(superClass);
283 String[] interfaceNames = new String[interfaces.length + 1];
284 for (int i = 0; i < interfaces.length; i++) {
285 interfaceNames[i] = toInternalClassName(interfaces[i]);
287 interfaceNames[interfaces.length] = toInternalClassName(InternalJavaProxy.class);
289 // start class
290 cw.visit(Opcodes.V1_3, access, name, signature, supername,
291 interfaceNames);
293 cw.visitField(Opcodes.ACC_PRIVATE, INVOCATION_HANDLER_FIELD_NAME,
294 INVOCATION_HANDLER_TYPE.getDescriptor(), null, null).visitEnd();
296 cw.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC,
297 PROXY_CLASS_FIELD_NAME, PROXY_CLASS_TYPE.getDescriptor(), null,
298 null).visitEnd();
300 return cw;
303 private static void generateProxyMethods(Class superClass, Map methods,
304 Type selfType, ClassVisitor cw, GeneratorAdapter clazzInit) {
305 Iterator it = methods.values().iterator();
306 while (it.hasNext()) {
307 MethodData md = (MethodData) it.next();
308 Type superClassType = Type.getType(superClass);
309 generateProxyMethod(selfType, superClassType, cw, clazzInit, md);
313 private static void generateGetInvocationHandler(Type selfType,
314 ClassVisitor cw) {
315 // make getter for handler
316 GeneratorAdapter gh = new GeneratorAdapter(Opcodes.ACC_PUBLIC,
317 new org.objectweb.asm.commons.Method("___getInvocationHandler",
318 INVOCATION_HANDLER_TYPE, EMPTY_TYPE_ARR), null,
319 EMPTY_TYPE_ARR, cw);
321 gh.loadThis();
322 gh.getField(selfType, INVOCATION_HANDLER_FIELD_NAME,
323 INVOCATION_HANDLER_TYPE);
324 gh.returnValue();
325 gh.endMethod();
328 private static void generateGetProxyClass(Type selfType, ClassVisitor cw) {
329 // make getter for proxy class
330 GeneratorAdapter gpc = new GeneratorAdapter(Opcodes.ACC_PUBLIC,
331 new org.objectweb.asm.commons.Method("___getProxyClass",
332 PROXY_CLASS_TYPE, EMPTY_TYPE_ARR), null,
333 EMPTY_TYPE_ARR, cw);
334 gpc.getStatic(selfType, PROXY_CLASS_FIELD_NAME, PROXY_CLASS_TYPE);
335 gpc.returnValue();
336 gpc.endMethod();
339 private static void generateConstructors(Class superClass, Type selfType,
340 ClassVisitor cw) {
341 Constructor[] cons = superClass.getDeclaredConstructors();
342 for (int i = 0; i < cons.length; i++) {
343 Constructor constructor = cons[i];
345 int acc = constructor.getModifiers();
346 if (Modifier.isProtected(acc) || Modifier.isPublic(acc)) {
347 // ok, it's publix or protected
348 } else if (!Modifier.isPrivate(acc)
349 && packageName(constructor.getDeclaringClass()).equals(
350 packageName(selfType.getClassName()))) {
351 // ok, it's package scoped and we're in the same package
352 } else {
353 // it's unaccessible
354 continue;
357 generateConstructor(selfType, constructor, cw);
361 private static GeneratorAdapter createClassInitializer(Type selfType,
362 ClassVisitor cw) {
363 GeneratorAdapter clazzInit;
364 clazzInit = new GeneratorAdapter(Opcodes.ACC_PRIVATE
365 | Opcodes.ACC_STATIC, new org.objectweb.asm.commons.Method(
366 "<clinit>", Type.VOID_TYPE, EMPTY_TYPE_ARR), null,
367 EMPTY_TYPE_ARR, cw);
369 clazzInit.visitLdcInsn(selfType.getClassName());
370 clazzInit.invokeStatic(JAVA_LANG_CLASS_TYPE, CLASS_FORNAME_METHOD);
371 clazzInit
372 .invokeStatic(PROXY_HELPER_TYPE, HELPER_GET_PROXY_CLASS_METHOD);
373 clazzInit.dup();
374 clazzInit.putStatic(selfType, PROXY_CLASS_FIELD_NAME, PROXY_CLASS_TYPE);
375 return clazzInit;
378 private static void generateProxyMethod(Type selfType, Type superType,
379 ClassVisitor cw, GeneratorAdapter clazzInit, MethodData md) {
380 if (!md.generateProxyMethod()) {
381 return;
384 org.objectweb.asm.commons.Method m = md.getMethod();
385 Type[] ex = toType(md.getExceptions());
387 String field_name = "__mth$" + md.getName() + md.scrambledSignature();
389 // create static private method field
390 FieldVisitor fv = cw.visitField(Opcodes.ACC_PRIVATE
391 | Opcodes.ACC_STATIC, field_name, PROXY_METHOD_TYPE
392 .getDescriptor(), null, null);
393 fv.visitEnd();
395 clazzInit.dup();
396 clazzInit.push(m.getName());
397 clazzInit.push(m.getDescriptor());
398 clazzInit.push(md.isImplemented());
399 clazzInit.invokeStatic(PROXY_HELPER_TYPE, PROXY_HELPER_GET_METHOD);
400 clazzInit.putStatic(selfType, field_name, PROXY_METHOD_TYPE);
402 org.objectweb.asm.commons.Method sm = new org.objectweb.asm.commons.Method(
403 "__super$" + m.getName(), m.getReturnType(), m
404 .getArgumentTypes());
407 // construct the proxy method
409 GeneratorAdapter ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC, m, null,
410 ex, cw);
412 ga.loadThis();
413 ga.getField(selfType, INVOCATION_HANDLER_FIELD_NAME,
414 INVOCATION_HANDLER_TYPE);
416 // if the method is extending something, then we have
417 // to test if the handler is initialized...
419 if (md.isImplemented()) {
420 ga.dup();
421 Label ok = ga.newLabel();
422 ga.ifNonNull(ok);
424 ga.loadThis();
425 ga.loadArgs();
426 ga.invokeConstructor(superType, m);
427 ga.returnValue();
428 ga.mark(ok);
431 ga.loadThis();
432 ga.getStatic(selfType, field_name, PROXY_METHOD_TYPE);
434 if (m.getArgumentTypes().length == 0) {
435 // load static empty array
436 ga.getStatic(JAVA_PROXY_TYPE, "NO_ARGS", Type
437 .getType(Object[].class));
438 } else {
439 // box arguments
440 ga.loadArgArray();
443 Label before = ga.mark();
445 ga.invokeInterface(INVOCATION_HANDLER_TYPE,
446 INVOCATION_HANDLER_INVOKE_METHOD);
448 Label after = ga.mark();
450 ga.unbox(m.getReturnType());
451 ga.returnValue();
453 // this is a simple rethrow handler
454 Label rethrow = ga.mark();
455 ga.visitInsn(Opcodes.ATHROW);
457 for (int i = 0; i < ex.length; i++) {
458 ga.visitTryCatchBlock(before, after, rethrow, ex[i]
459 .getInternalName());
462 ga.visitTryCatchBlock(before, after, rethrow, "java/lang/Error");
463 ga.visitTryCatchBlock(before, after, rethrow,
464 "java/lang/RuntimeException");
466 Type thr = Type.getType(Throwable.class);
467 Label handler = ga.mark();
468 Type udt = Type.getType(UndeclaredThrowableException.class);
469 int loc = ga.newLocal(thr);
470 ga.storeLocal(loc, thr);
471 ga.newInstance(udt);
472 ga.dup();
473 ga.loadLocal(loc, thr);
474 ga.invokeConstructor(udt, org.objectweb.asm.commons.Method
475 .getMethod("void <init>(java.lang.Throwable)"));
476 ga.throwException();
478 ga.visitTryCatchBlock(before, after, handler, "java/lang/Throwable");
480 ga.endMethod();
483 // construct the super-proxy method
485 if (md.isImplemented()) {
487 GeneratorAdapter ga2 = new GeneratorAdapter(Opcodes.ACC_PUBLIC, sm,
488 null, ex, cw);
490 ga2.loadThis();
491 ga2.loadArgs();
492 ga2.invokeConstructor(superType, m);
493 ga2.returnValue();
494 ga2.endMethod();
498 private static Class[] generateConstructor(Type selfType,
499 Constructor constructor, ClassVisitor cw) {
501 Class[] superConstructorParameterTypes = constructor
502 .getParameterTypes();
503 Class[] newConstructorParameterTypes = new Class[superConstructorParameterTypes.length + 1];
504 System.arraycopy(superConstructorParameterTypes, 0,
505 newConstructorParameterTypes, 0,
506 superConstructorParameterTypes.length);
507 newConstructorParameterTypes[superConstructorParameterTypes.length] = JavaProxyInvocationHandler.class;
509 int access = Opcodes.ACC_PUBLIC;
510 String name1 = "<init>";
511 String signature = null;
512 Class[] superConstructorExceptions = constructor.getExceptionTypes();
514 org.objectweb.asm.commons.Method super_m = new org.objectweb.asm.commons.Method(
515 name1, Type.VOID_TYPE, toType(superConstructorParameterTypes));
516 org.objectweb.asm.commons.Method m = new org.objectweb.asm.commons.Method(
517 name1, Type.VOID_TYPE, toType(newConstructorParameterTypes));
519 GeneratorAdapter ga = new GeneratorAdapter(access, m, signature,
520 toType(superConstructorExceptions), cw);
522 ga.loadThis();
523 ga.loadArgs(0, superConstructorParameterTypes.length);
524 ga.invokeConstructor(Type.getType(constructor.getDeclaringClass()),
525 super_m);
527 ga.loadThis();
528 ga.loadArg(superConstructorParameterTypes.length);
529 ga.putField(selfType, INVOCATION_HANDLER_FIELD_NAME,
530 INVOCATION_HANDLER_TYPE);
532 // do a void return
533 ga.returnValue();
534 ga.endMethod();
535 return newConstructorParameterTypes;
538 private static String toInternalClassName(Class clazz) {
539 return toInternalClassName(clazz.getName());
542 private static String toInternalClassName(String name) {
543 return name.replace('.', '/');
546 private static Type[] toType(Class[] parameterTypes) {
547 Type[] result = new Type[parameterTypes.length];
548 for (int i = 0; i < result.length; i++) {
549 result[i] = Type.getType(parameterTypes[i]);
551 return result;
554 private static void collectMethods(Class superClass, Class[] interfaces,
555 Map methods, Set names) {
556 HashSet allClasses = new HashSet();
557 addClass(allClasses, methods, superClass, names);
558 addInterfaces(allClasses, methods, interfaces, names);
561 static class MethodData {
562 Set methods = new HashSet();
564 final Method mostSpecificMethod;
565 final Class[] mostSpecificParameterTypes;
567 boolean hasPublicDecl = false;
569 MethodData(Method method) {
570 this.mostSpecificMethod = method;
571 this.mostSpecificParameterTypes = mostSpecificMethod.getParameterTypes();
572 hasPublicDecl = method.getDeclaringClass().isInterface()
573 || Modifier.isPublic(method.getModifiers());
576 public String scrambledSignature() {
577 StringBuilder sb = new StringBuilder();
578 Class[] parms = getParameterTypes();
579 for (int i = 0; i < parms.length; i++) {
580 sb.append('$');
581 String name = parms[i].getName();
582 name = name.replace('[', '1');
583 name = name.replace('.', '_');
584 name = name.replace(';', '2');
585 sb.append(name);
587 return sb.toString();
590 public Class getDeclaringClass() {
591 return mostSpecificMethod.getDeclaringClass();
594 public org.objectweb.asm.commons.Method getMethod() {
595 return new org.objectweb.asm.commons.Method(getName(), Type
596 .getType(getReturnType()), getType(getParameterTypes()));
599 private Type[] getType(Class[] parameterTypes) {
600 Type[] result = new Type[parameterTypes.length];
601 for (int i = 0; i < parameterTypes.length; i++) {
602 result[i] = Type.getType(parameterTypes[i]);
604 return result;
607 private String getName() {
608 return mostSpecificMethod.getName();
611 private Class[] getParameterTypes() {
612 return mostSpecificParameterTypes;
615 public Class[] getExceptions() {
617 Set all = new HashSet();
619 Iterator it = methods.iterator();
620 while (it.hasNext()) {
621 Method m = (Method) it.next();
622 Class[] ex = m.getExceptionTypes();
623 for (int i = 0; i < ex.length; i++) {
624 Class exx = ex[i];
626 if (all.contains(exx)) {
627 continue;
630 boolean add = true;
631 Iterator it2 = all.iterator();
632 while (it2.hasNext()) {
633 Class de = (Class) it2.next();
635 if (de.isAssignableFrom(exx)) {
636 add = false;
637 break;
638 } else if (exx.isAssignableFrom(de)) {
639 it2.remove();
640 add = true;
645 if (add) {
646 all.add(exx);
651 return (Class[]) all.toArray(new Class[all.size()]);
654 public boolean generateProxyMethod() {
655 return !isFinal() && !isPrivate();
658 public void add(Method method) {
659 methods.add(method);
660 hasPublicDecl |= Modifier.isPublic(method.getModifiers());
663 Class getReturnType() {
664 return mostSpecificMethod.getReturnType();
667 boolean isFinal() {
668 if (mostSpecificMethod.getDeclaringClass().isInterface()) {
669 return false;
672 int mod = mostSpecificMethod.getModifiers();
673 return Modifier.isFinal(mod);
676 boolean isPrivate() {
677 if (mostSpecificMethod.getDeclaringClass().isInterface()) {
678 return false;
681 int mod = mostSpecificMethod.getModifiers();
682 return Modifier.isPrivate(mod);
685 boolean isImplemented() {
686 if (mostSpecificMethod.getDeclaringClass().isInterface()) {
687 return false;
690 int mod = mostSpecificMethod.getModifiers();
691 return !Modifier.isAbstract(mod);
695 static class MethodKey {
696 private String name;
698 private Class[] arguments;
700 MethodKey(Method m) {
701 this.name = m.getName();
702 this.arguments = m.getParameterTypes();
705 public boolean equals(Object obj) {
706 if (obj instanceof MethodKey) {
707 MethodKey key = (MethodKey) obj;
709 return name.equals(key.name)
710 && Arrays.equals(arguments, key.arguments);
713 return false;
716 public int hashCode() {
717 return name.hashCode();
721 private static void addInterfaces(Set allClasses, Map methods,
722 Class[] interfaces, Set names) {
723 for (int i = 0; i < interfaces.length; i++) {
724 addInterface(allClasses, methods, interfaces[i], names);
728 private static void addInterface(Set allClasses, Map methods,
729 Class interfaze, Set names) {
730 if (allClasses.add(interfaze)) {
731 addMethods(methods, interfaze, names);
732 addInterfaces(allClasses, methods, interfaze.getInterfaces(), names);
736 private static void addMethods(Map methods, Class classOrInterface, Set names) {
737 Method[] mths = classOrInterface.getDeclaredMethods();
738 for (int i = 0; i < mths.length; i++) {
739 if (names == null || names.contains(mths[i].getName())) {
740 addMethod(methods, mths[i]);
745 private static void addMethod(Map methods, Method method) {
746 int acc = method.getModifiers();
748 if (Modifier.isStatic(acc) || Modifier.isPrivate(acc)) {
749 return;
752 MethodKey mk = new MethodKey(method);
753 MethodData md = (MethodData) methods.get(mk);
754 if (md == null) {
755 md = new MethodData(method);
756 methods.put(mk, md);
758 md.add(method);
761 private static void addClass(Set allClasses, Map methods, Class clazz, Set names) {
762 if (allClasses.add(clazz)) {
763 addMethods(methods, clazz, names);
764 Class superClass = clazz.getSuperclass();
765 if (superClass != null) {
766 addClass(allClasses, methods, superClass, names);
769 addInterfaces(allClasses, methods, clazz.getInterfaces(), names);
773 private static void validateArgs(Ruby runtime, String targetClassName, Class superClass) {
775 if (Modifier.isFinal(superClass.getModifiers())) {
776 throw runtime.newTypeError("cannot extend final class " + superClass.getName());
779 String targetPackage = packageName(targetClassName);
781 String pkg = targetPackage.replace('.', '/');
782 if (pkg.startsWith("java")) {
783 throw runtime.newTypeError("cannot add classes to package " + pkg);
786 Package p = Package.getPackage(pkg);
787 if (p != null) {
788 if (p.isSealed()) {
789 throw runtime.newTypeError("package " + p + " is sealed");
794 private static String packageName(Class clazz) {
795 String clazzName = clazz.getName();
796 return packageName(clazzName);
799 private static String packageName(String clazzName) {
800 int idx = clazzName.lastIndexOf('.');
801 if (idx == -1) {
802 return "";
803 } else {
804 return clazzName.substring(0, idx);