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
;
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() {
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();
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
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
;
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
);
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
,
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();
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
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();
236 AccessController
.doPrivileged(new PrivilegedAction() {
237 public Object
run() {
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!
247 defineClass_method
.setAccessible(true);
253 private static Class
invokeDefineClass(ClassLoader loader
,
254 String className
, byte[] data
) {
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
263 } catch (IllegalAccessException e
) {
264 // TODO Auto-generated catch block
267 } catch (InvocationTargetException e
) {
268 // TODO Auto-generated catch block
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);
290 cw
.visit(Opcodes
.V1_3
, access
, name
, signature
, supername
,
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,
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
,
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,
322 gh
.getField(selfType
, INVOCATION_HANDLER_FIELD_NAME
,
323 INVOCATION_HANDLER_TYPE
);
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,
334 gpc
.getStatic(selfType
, PROXY_CLASS_FIELD_NAME
, PROXY_CLASS_TYPE
);
339 private static void generateConstructors(Class superClass
, Type selfType
,
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
357 generateConstructor(selfType
, constructor
, cw
);
361 private static GeneratorAdapter
createClassInitializer(Type selfType
,
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,
369 clazzInit
.visitLdcInsn(selfType
.getClassName());
370 clazzInit
.invokeStatic(JAVA_LANG_CLASS_TYPE
, CLASS_FORNAME_METHOD
);
372 .invokeStatic(PROXY_HELPER_TYPE
, HELPER_GET_PROXY_CLASS_METHOD
);
374 clazzInit
.putStatic(selfType
, PROXY_CLASS_FIELD_NAME
, PROXY_CLASS_TYPE
);
378 private static void generateProxyMethod(Type selfType
, Type superType
,
379 ClassVisitor cw
, GeneratorAdapter clazzInit
, MethodData md
) {
380 if (!md
.generateProxyMethod()) {
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);
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,
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()) {
421 Label ok
= ga
.newLabel();
426 ga
.invokeConstructor(superType
, m
);
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));
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());
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
]
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
);
473 ga
.loadLocal(loc
, thr
);
474 ga
.invokeConstructor(udt
, org
.objectweb
.asm
.commons
.Method
475 .getMethod("void <init>(java.lang.Throwable)"));
478 ga
.visitTryCatchBlock(before
, after
, handler
, "java/lang/Throwable");
483 // construct the super-proxy method
485 if (md
.isImplemented()) {
487 GeneratorAdapter ga2
= new GeneratorAdapter(Opcodes
.ACC_PUBLIC
, sm
,
492 ga2
.invokeConstructor(superType
, m
);
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
);
523 ga
.loadArgs(0, superConstructorParameterTypes
.length
);
524 ga
.invokeConstructor(Type
.getType(constructor
.getDeclaringClass()),
528 ga
.loadArg(superConstructorParameterTypes
.length
);
529 ga
.putField(selfType
, INVOCATION_HANDLER_FIELD_NAME
,
530 INVOCATION_HANDLER_TYPE
);
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
]);
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
++) {
581 String name
= parms
[i
].getName();
582 name
= name
.replace('[', '1');
583 name
= name
.replace('.', '_');
584 name
= name
.replace(';', '2');
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
]);
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
++) {
626 if (all
.contains(exx
)) {
631 Iterator it2
= all
.iterator();
632 while (it2
.hasNext()) {
633 Class de
= (Class
) it2
.next();
635 if (de
.isAssignableFrom(exx
)) {
638 } else if (exx
.isAssignableFrom(de
)) {
651 return (Class
[]) all
.toArray(new Class
[all
.size()]);
654 public boolean generateProxyMethod() {
655 return !isFinal() && !isPrivate();
658 public void add(Method method
) {
660 hasPublicDecl
|= Modifier
.isPublic(method
.getModifiers());
663 Class
getReturnType() {
664 return mostSpecificMethod
.getReturnType();
668 if (mostSpecificMethod
.getDeclaringClass().isInterface()) {
672 int mod
= mostSpecificMethod
.getModifiers();
673 return Modifier
.isFinal(mod
);
676 boolean isPrivate() {
677 if (mostSpecificMethod
.getDeclaringClass().isInterface()) {
681 int mod
= mostSpecificMethod
.getModifiers();
682 return Modifier
.isPrivate(mod
);
685 boolean isImplemented() {
686 if (mostSpecificMethod
.getDeclaringClass().isInterface()) {
690 int mod
= mostSpecificMethod
.getModifiers();
691 return !Modifier
.isAbstract(mod
);
695 static class MethodKey
{
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
);
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
)) {
752 MethodKey mk
= new MethodKey(method
);
753 MethodData md
= (MethodData
) methods
.get(mk
);
755 md
= new MethodData(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
);
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('.');
804 return clazzName
.substring(0, idx
);