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) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
15 * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
16 * Copyright (C) 2004-2005 Thomas E Enebo <enebo@acm.org>
17 * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
18 * Copyright (C) 2004 David Corbin <dcorbin@users.sourceforge.net>
19 * Copyright (C) 2005 Charles O Nutter <headius@headius.com>
20 * Copyright (C) 2006 Kresten Krab Thorup <krab@gnu.org>
21 * Copyright (C) 2007 Miguel Covarrubias <mlcovarrubias@gmail.com>
22 * Copyright (C) 2007 William N Dortch <bill.dortch@gmail.com>
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the CPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the CPL, the GPL or the LGPL.
35 ***** END LICENSE BLOCK *****/
36 package org
.jruby
.javasupport
;
38 import org
.jruby
.java
.invokers
.StaticFieldGetter
;
39 import org
.jruby
.java
.invokers
.StaticMethodInvoker
;
40 import org
.jruby
.java
.invokers
.InstanceFieldGetter
;
41 import org
.jruby
.java
.invokers
.InstanceFieldSetter
;
42 import org
.jruby
.java
.invokers
.InstanceMethodInvoker
;
43 import org
.jruby
.java
.invokers
.StaticFieldSetter
;
44 import java
.io
.ByteArrayOutputStream
;
45 import java
.io
.InputStream
;
46 import java
.io
.IOException
;
47 import java
.lang
.reflect
.Array
;
48 import java
.lang
.reflect
.Constructor
;
49 import java
.lang
.reflect
.Field
;
50 import java
.lang
.reflect
.Method
;
51 import java
.lang
.reflect
.Modifier
;
52 import java
.util
.ArrayList
;
53 import java
.util
.HashMap
;
54 import java
.util
.Iterator
;
55 import java
.util
.List
;
57 import java
.util
.concurrent
.locks
.ReentrantLock
;
58 import java
.util
.regex
.Matcher
;
59 import java
.util
.regex
.Pattern
;
61 import org
.jruby
.Ruby
;
62 import org
.jruby
.RubyArray
;
63 import org
.jruby
.RubyBoolean
;
64 import org
.jruby
.RubyClass
;
65 import org
.jruby
.RubyFixnum
;
66 import org
.jruby
.RubyInteger
;
67 import org
.jruby
.RubyModule
;
68 import org
.jruby
.RubyString
;
69 import org
.jruby
.RubySymbol
;
70 import org
.jruby
.anno
.JRubyMethod
;
71 import org
.jruby
.anno
.JRubyClass
;
72 import org
.jruby
.common
.IRubyWarnings
.ID
;
73 import org
.jruby
.exceptions
.RaiseException
;
74 import org
.jruby
.internal
.runtime
.methods
.DynamicMethod
;
75 import org
.jruby
.java
.proxies
.ArrayJavaProxy
;
76 import org
.jruby
.java
.invokers
.ConstructorInvoker
;
77 import org
.jruby
.java
.invokers
.DynalangInstanceInvoker
;
78 import org
.jruby
.javasupport
.util
.RuntimeHelpers
;
79 import org
.jruby
.runtime
.Arity
;
80 import org
.jruby
.runtime
.Block
;
81 import org
.jruby
.runtime
.CallType
;
82 import org
.jruby
.runtime
.ObjectAllocator
;
83 import org
.jruby
.runtime
.ThreadContext
;
84 import org
.jruby
.runtime
.Visibility
;
85 import org
.jruby
.runtime
.builtin
.IRubyObject
;
86 import org
.jruby
.runtime
.callback
.Callback
;
87 import org
.jruby
.util
.ByteList
;
88 import org
.jruby
.util
.IdUtil
;
89 import org
.jruby
.util
.SafePropertyAccessor
;
91 @JRubyClass(name
="Java::JavaClass", parent
="Java::JavaObject")
92 public class JavaClass
extends JavaObject
{
94 // some null objects to simplify later code
95 private static final Class
<?
>[] EMPTY_CLASS_ARRAY
= new Class
<?
>[] {};
96 private static final Method
[] EMPTY_METHOD_ARRAY
= new Method
[] {};
97 private static final Constructor
[] EMPTY_CONSTRUCTOR_ARRAY
= new Constructor
[] {};
98 private static final Field
[] EMPTY_FIELD_ARRAY
= new Field
[] {};
100 private static class AssignedName
{
101 // to override an assigned name, the type must be less than
102 // or equal to the assigned type. so a field name in a subclass
103 // will override an alias in a superclass, but not a method.
104 static final int RESERVED
= 0;
105 static final int METHOD
= 1;
106 static final int FIELD
= 2;
107 static final int PROTECTED_METHOD
= 3;
108 static final int WEAKLY_RESERVED
= 4; // we'll be peeved, but not devastated, if you override
109 static final int ALIAS
= 5;
110 // yes, protected fields are weaker than aliases. many conflicts
111 // in the old AWT code, for example, where you really want 'size'
112 // to mean the public method getSize, not the protected field 'size'.
113 static final int PROTECTED_FIELD
= 6;
117 AssignedName(String name
, int type
) {
123 // TODO: other reserved names?
124 private static final Map
<String
, AssignedName
> RESERVED_NAMES
= new HashMap
<String
, AssignedName
>();
126 RESERVED_NAMES
.put("__id__", new AssignedName("__id__", AssignedName
.RESERVED
));
127 RESERVED_NAMES
.put("__send__", new AssignedName("__send__", AssignedName
.RESERVED
));
128 RESERVED_NAMES
.put("class", new AssignedName("class", AssignedName
.RESERVED
));
129 RESERVED_NAMES
.put("initialize", new AssignedName("initialize", AssignedName
.RESERVED
));
130 RESERVED_NAMES
.put("object_id", new AssignedName("object_id", AssignedName
.RESERVED
));
131 RESERVED_NAMES
.put("private", new AssignedName("private", AssignedName
.RESERVED
));
132 RESERVED_NAMES
.put("protected", new AssignedName("protected", AssignedName
.RESERVED
));
133 RESERVED_NAMES
.put("public", new AssignedName("public", AssignedName
.RESERVED
));
135 // weakly reserved names
136 RESERVED_NAMES
.put("id", new AssignedName("id", AssignedName
.WEAKLY_RESERVED
));
138 private static final Map
<String
, AssignedName
> STATIC_RESERVED_NAMES
= new HashMap
<String
, AssignedName
>(RESERVED_NAMES
);
140 STATIC_RESERVED_NAMES
.put("new", new AssignedName("new", AssignedName
.RESERVED
));
142 private static final Map
<String
, AssignedName
> INSTANCE_RESERVED_NAMES
= new HashMap
<String
, AssignedName
>(RESERVED_NAMES
);
144 private static abstract class NamedInstaller
{
145 static final int STATIC_FIELD
= 1;
146 static final int STATIC_METHOD
= 2;
147 static final int INSTANCE_FIELD
= 3;
148 static final int INSTANCE_METHOD
= 4;
149 static final int CONSTRUCTOR
= 5;
152 Visibility visibility
= Visibility
.PUBLIC
;
155 NamedInstaller (String name
, int type
) {
159 abstract void install(RubyClass proxy
);
160 // small hack to save a cast later on
161 boolean hasLocalMethod() {
165 return visibility
== Visibility
.PUBLIC
;
167 boolean isProtected() {
168 return visibility
== Visibility
.PROTECTED
;
172 private static abstract class FieldInstaller
extends NamedInstaller
{
175 FieldInstaller(String name
, int type
, Field field
) {
181 private static class StaticFieldGetterInstaller
extends FieldInstaller
{
182 StaticFieldGetterInstaller(){}
183 StaticFieldGetterInstaller(String name
, Field field
) {
184 super(name
,STATIC_FIELD
,field
);
186 void install(RubyClass proxy
) {
187 if (Modifier
.isPublic(field
.getModifiers())) {
188 proxy
.getSingletonClass().addMethod(name
, new StaticFieldGetter(name
, proxy
, field
));
193 private static class StaticFieldSetterInstaller
extends FieldInstaller
{
194 StaticFieldSetterInstaller(){}
195 StaticFieldSetterInstaller(String name
, Field field
) {
196 super(name
,STATIC_FIELD
,field
);
198 void install(RubyClass proxy
) {
199 if (Modifier
.isPublic(field
.getModifiers())) {
200 proxy
.getSingletonClass().addMethod(name
, new StaticFieldSetter(name
, proxy
, field
));
205 private static class InstanceFieldGetterInstaller
extends FieldInstaller
{
206 InstanceFieldGetterInstaller(){}
207 InstanceFieldGetterInstaller(String name
, Field field
) {
208 super(name
,INSTANCE_FIELD
,field
);
210 void install(RubyClass proxy
) {
211 if (Modifier
.isPublic(field
.getModifiers())) {
212 proxy
.addMethod(name
, new InstanceFieldGetter(name
, proxy
, field
));
217 private static class InstanceFieldSetterInstaller
extends FieldInstaller
{
218 InstanceFieldSetterInstaller(){}
219 InstanceFieldSetterInstaller(String name
, Field field
) {
220 super(name
,INSTANCE_FIELD
,field
);
222 void install(RubyClass proxy
) {
223 if (Modifier
.isPublic(field
.getModifiers())) {
224 proxy
.addMethod(name
, new InstanceFieldSetter(name
, proxy
, field
));
229 private static abstract class MethodInstaller
extends NamedInstaller
{
230 private boolean haveLocalMethod
;
231 protected List
<Method
> methods
;
232 protected List
<String
> aliases
;
234 MethodInstaller(String name
, int type
) {
238 // called only by initializing thread; no synchronization required
239 void addMethod(Method method
, Class
<?
> javaClass
) {
240 if (methods
== null) {
241 methods
= new ArrayList
<Method
>();
243 if (!Ruby
.isSecurityRestricted()) {
244 method
.setAccessible(true);
247 haveLocalMethod
|= javaClass
== method
.getDeclaringClass();
250 // called only by initializing thread; no synchronization required
251 void addAlias(String alias
) {
252 if (aliases
== null) {
253 aliases
= new ArrayList
<String
>();
255 if (!aliases
.contains(alias
))
259 // modified only by addMethod; no synchronization required
260 boolean hasLocalMethod () {
261 return haveLocalMethod
;
265 private static class ConstructorInvokerInstaller
extends MethodInstaller
{
266 private boolean haveLocalConstructor
;
267 protected List
<Constructor
> constructors
;
269 ConstructorInvokerInstaller(String name
) {
270 super(name
,STATIC_METHOD
);
273 // called only by initializing thread; no synchronization required
274 void addConstructor(Constructor ctor
, Class
<?
> javaClass
) {
275 if (constructors
== null) {
276 constructors
= new ArrayList
<Constructor
>();
278 if (!Ruby
.isSecurityRestricted()) {
279 ctor
.setAccessible(true);
281 constructors
.add(ctor
);
282 haveLocalConstructor
|= javaClass
== ctor
.getDeclaringClass();
285 void install(RubyClass proxy
) {
286 if (haveLocalConstructor
) {
287 DynamicMethod method
= new ConstructorInvoker(proxy
, constructors
);
288 proxy
.addMethod(name
, method
);
293 private static class StaticMethodInvokerInstaller
extends MethodInstaller
{
294 StaticMethodInvokerInstaller(String name
) {
295 super(name
,STATIC_METHOD
);
298 void install(RubyClass proxy
) {
299 if (hasLocalMethod()) {
300 RubyClass singleton
= proxy
.getSingletonClass();
301 DynamicMethod method
= new StaticMethodInvoker(singleton
, methods
);
302 singleton
.addMethod(name
, method
);
303 if (aliases
!= null && isPublic() ) {
304 singleton
.defineAliases(aliases
, this.name
);
311 private static class InstanceMethodInvokerInstaller
extends MethodInstaller
{
312 InstanceMethodInvokerInstaller(String name
) {
313 super(name
,INSTANCE_METHOD
);
315 void install(RubyClass proxy
) {
316 if (hasLocalMethod()) {
317 DynamicMethod method
;
318 if (SafePropertyAccessor
.getBoolean("jruby.dynalang.enabled", false)) {
319 method
= new DynalangInstanceInvoker(proxy
, methods
);
321 method
= new InstanceMethodInvoker(proxy
, methods
);
323 proxy
.addMethod(name
, method
);
324 if (aliases
!= null && isPublic()) {
325 proxy
.defineAliases(aliases
, this.name
);
332 private static class ConstantField
{
333 static final int CONSTANT
= Modifier
.FINAL
| Modifier
.PUBLIC
| Modifier
.STATIC
;
335 ConstantField(Field field
) {
338 void install(final RubyModule proxy
) {
339 if (proxy
.fastGetConstantAt(field
.getName()) == null) {
340 // TODO: catch exception if constant is already set by other
342 if (!Ruby
.isSecurityRestricted()) {
343 field
.setAccessible(true);
346 proxy
.setConstant(field
.getName(), JavaUtil
.convertJavaToUsableRubyObject(proxy
.getRuntime(), field
.get(null)));
347 } catch (IllegalAccessException iae
) {
348 throw proxy
.getRuntime().newTypeError(
349 "illegal access on setting variable: " + iae
.getMessage());
353 static boolean isConstant(final Field field
) {
354 return (field
.getModifiers() & CONSTANT
) == CONSTANT
&&
355 Character
.isUpperCase(field
.getName().charAt(0));
359 private final RubyModule JAVA_UTILITIES
= getRuntime().getJavaSupport().getJavaUtilitiesModule();
361 private Map
<String
, AssignedName
> staticAssignedNames
;
362 private Map
<String
, AssignedName
> instanceAssignedNames
;
363 private Map
<String
, NamedInstaller
> staticInstallers
;
364 private Map
<String
, NamedInstaller
> instanceInstallers
;
365 private ConstructorInvokerInstaller constructorInstaller
;
366 private List
<ConstantField
> constantFields
;
367 // caching constructors, as they're accessed for each new instance
368 private volatile RubyArray constructors
;
370 private volatile ArrayList
<IRubyObject
> proxyExtenders
;
372 // proxy module for interfaces
373 private volatile RubyModule proxyModule
;
375 // proxy class for concrete classes. also used for
376 // "concrete" interfaces, which is why we have two fields
377 private volatile RubyClass proxyClass
;
379 // readable only by thread building proxy, so don't need to be
380 // volatile. used to handle recursive calls to getProxyClass/Module
381 // while proxy is being constructed (usually when a constant
382 // defined by a class is of the same type as that class).
383 private RubyModule unfinishedProxyModule
;
384 private RubyClass unfinishedProxyClass
;
386 private final ReentrantLock proxyLock
= new ReentrantLock();
388 public RubyModule
getProxyModule() {
389 // allow proxy to be read without synchronization. if proxy
390 // is under construction, only the building thread can see it.
392 if ((proxy
= proxyModule
) != null) {
393 // proxy is complete, return it
395 } else if (proxyLock
.isHeldByCurrentThread()) {
396 // proxy is under construction, building thread can
397 // safely read non-volatile value
398 return unfinishedProxyModule
;
403 public RubyClass
getProxyClass() {
404 // allow proxy to be read without synchronization. if proxy
405 // is under construction, only the building thread can see it.
407 if ((proxy
= proxyClass
) != null) {
408 // proxy is complete, return it
410 } else if (proxyLock
.isHeldByCurrentThread()) {
411 // proxy is under construction, building thread can
412 // safely read non-volatile value
413 return unfinishedProxyClass
;
418 public void lockProxy() {
422 public void unlockProxy() {
426 protected Map
<String
, AssignedName
> getStaticAssignedNames() {
427 return staticAssignedNames
;
429 protected Map
<String
, AssignedName
> getInstanceAssignedNames() {
430 return instanceAssignedNames
;
433 private JavaClass(Ruby runtime
, Class
<?
> javaClass
) {
434 super(runtime
, (RubyClass
) runtime
.getJavaSupport().getJavaClassClass(), javaClass
);
435 if (javaClass
.isInterface()) {
436 initializeInterface(javaClass
);
437 } else if (!(javaClass
.isArray() || javaClass
.isPrimitive())) {
438 // TODO: public only?
439 initializeClass(javaClass
);
443 public boolean equals(Object other
) {
444 return other
instanceof JavaClass
&&
445 this.getValue() == ((JavaClass
)other
).getValue();
448 private void initializeInterface(Class
<?
> javaClass
) {
449 Map
<String
, AssignedName
> staticNames
= new HashMap
<String
, AssignedName
>(STATIC_RESERVED_NAMES
);
450 List
<ConstantField
> constantFields
= new ArrayList
<ConstantField
>();
451 Field
[] fields
= EMPTY_FIELD_ARRAY
;
453 fields
= javaClass
.getDeclaredFields();
454 } catch (SecurityException e
) {
456 fields
= javaClass
.getFields();
457 } catch (SecurityException e2
) {
460 for (int i
= fields
.length
; --i
>= 0; ) {
461 Field field
= fields
[i
];
462 if (javaClass
!= field
.getDeclaringClass()) continue;
463 if (ConstantField
.isConstant(field
)) {
464 constantFields
.add(new ConstantField(field
));
467 this.staticAssignedNames
= staticNames
;
468 this.constantFields
= constantFields
;
471 private void initializeClass(Class
<?
> javaClass
) {
472 Class
<?
> superclass
= javaClass
.getSuperclass();
473 Map
<String
, AssignedName
> staticNames
;
474 Map
<String
, AssignedName
> instanceNames
;
475 if (superclass
== null) {
476 staticNames
= new HashMap
<String
, AssignedName
>();
477 instanceNames
= new HashMap
<String
, AssignedName
>();
479 JavaClass superJavaClass
= get(getRuntime(),superclass
);
480 staticNames
= new HashMap
<String
, AssignedName
>(superJavaClass
.getStaticAssignedNames());
481 instanceNames
= new HashMap
<String
, AssignedName
>(superJavaClass
.getInstanceAssignedNames());
483 staticNames
.putAll(STATIC_RESERVED_NAMES
);
484 instanceNames
.putAll(INSTANCE_RESERVED_NAMES
);
485 Map
<String
, NamedInstaller
> staticCallbacks
= new HashMap
<String
, NamedInstaller
>();
486 Map
<String
, NamedInstaller
> instanceCallbacks
= new HashMap
<String
, NamedInstaller
>();
487 List
<ConstantField
> constantFields
= new ArrayList
<ConstantField
>();
488 Field
[] fields
= EMPTY_FIELD_ARRAY
;
490 fields
= javaClass
.getFields();
491 } catch (SecurityException e
) {
493 for (int i
= fields
.length
; --i
>= 0; ) {
494 Field field
= fields
[i
];
495 if (javaClass
!= field
.getDeclaringClass()) continue;
497 if (ConstantField
.isConstant(field
)) {
498 constantFields
.add(new ConstantField(field
));
501 String name
= field
.getName();
502 int modifiers
= field
.getModifiers();
503 if (Modifier
.isStatic(modifiers
)) {
504 AssignedName assignedName
= staticNames
.get(name
);
505 if (assignedName
!= null && assignedName
.type
< AssignedName
.FIELD
)
507 staticNames
.put(name
,new AssignedName(name
,AssignedName
.FIELD
));
508 staticCallbacks
.put(name
,new StaticFieldGetterInstaller(name
,field
));
509 if (!Modifier
.isFinal(modifiers
)) {
510 String setName
= name
+ '=';
511 staticCallbacks
.put(setName
,new StaticFieldSetterInstaller(setName
,field
));
514 AssignedName assignedName
= instanceNames
.get(name
);
515 if (assignedName
!= null && assignedName
.type
< AssignedName
.FIELD
)
517 instanceNames
.put(name
, new AssignedName(name
,AssignedName
.FIELD
));
518 instanceCallbacks
.put(name
, new InstanceFieldGetterInstaller(name
,field
));
519 if (!Modifier
.isFinal(modifiers
)) {
520 String setName
= name
+ '=';
521 instanceCallbacks
.put(setName
, new InstanceFieldSetterInstaller(setName
,field
));
525 // TODO: protected methods. this is going to require a rework
526 // of some of the mechanism.
527 Method
[] methods
= EMPTY_METHOD_ARRAY
;
528 for (Class c
= javaClass
; c
!= null; c
= c
.getSuperclass()) {
530 methods
= javaClass
.getMethods();
532 } catch (SecurityException e
) {
535 for (int i
= methods
.length
; --i
>= 0; ) {
536 // we need to collect all methods, though we'll only
537 // install the ones that are named in this class
538 Method method
= methods
[i
];
539 String name
= method
.getName();
540 if (Modifier
.isStatic(method
.getModifiers())) {
541 AssignedName assignedName
= staticNames
.get(name
);
542 if (assignedName
== null) {
543 staticNames
.put(name
,new AssignedName(name
,AssignedName
.METHOD
));
545 if (assignedName
.type
< AssignedName
.METHOD
)
547 if (assignedName
.type
!= AssignedName
.METHOD
) {
548 staticCallbacks
.remove(name
);
549 staticCallbacks
.remove(name
+'=');
550 staticNames
.put(name
,new AssignedName(name
,AssignedName
.METHOD
));
553 StaticMethodInvokerInstaller invoker
= (StaticMethodInvokerInstaller
)staticCallbacks
.get(name
);
554 if (invoker
== null) {
555 invoker
= new StaticMethodInvokerInstaller(name
);
556 staticCallbacks
.put(name
,invoker
);
558 invoker
.addMethod(method
,javaClass
);
560 AssignedName assignedName
= instanceNames
.get(name
);
561 if (assignedName
== null) {
562 instanceNames
.put(name
,new AssignedName(name
,AssignedName
.METHOD
));
564 if (assignedName
.type
< AssignedName
.METHOD
)
566 if (assignedName
.type
!= AssignedName
.METHOD
) {
567 instanceCallbacks
.remove(name
);
568 instanceCallbacks
.remove(name
+'=');
569 instanceNames
.put(name
,new AssignedName(name
,AssignedName
.METHOD
));
572 InstanceMethodInvokerInstaller invoker
= (InstanceMethodInvokerInstaller
)instanceCallbacks
.get(name
);
573 if (invoker
== null) {
574 invoker
= new InstanceMethodInvokerInstaller(name
);
575 instanceCallbacks
.put(name
,invoker
);
577 invoker
.addMethod(method
,javaClass
);
580 // TODO: protected methods. this is going to require a rework
581 // of some of the mechanism.
582 Constructor
[] constructors
= EMPTY_CONSTRUCTOR_ARRAY
;
584 constructors
= javaClass
.getConstructors();
585 } catch (SecurityException e
) {
587 for (int i
= constructors
.length
; --i
>= 0; ) {
588 // we need to collect all methods, though we'll only
589 // install the ones that are named in this class
590 Constructor ctor
= constructors
[i
];
592 if (constructorInstaller
== null) {
593 constructorInstaller
= new ConstructorInvokerInstaller("__jcreate!");
595 constructorInstaller
.addConstructor(ctor
,javaClass
);
598 this.staticAssignedNames
= staticNames
;
599 this.instanceAssignedNames
= instanceNames
;
600 this.staticInstallers
= staticCallbacks
;
601 this.instanceInstallers
= instanceCallbacks
;
602 this.constantFields
= constantFields
;
605 public void setupProxy(final RubyClass proxy
) {
606 assert proxyLock
.isHeldByCurrentThread();
607 proxy
.defineFastMethod("__jsend!", __jsend_method
);
608 final Class
<?
> javaClass
= javaClass();
609 if (javaClass
.isInterface()) {
610 setupInterfaceProxy(proxy
);
613 assert this.proxyClass
== null;
614 this.unfinishedProxyClass
= proxy
;
615 if (javaClass
.isArray() || javaClass
.isPrimitive()) {
616 // see note below re: 2-field kludge
617 this.proxyClass
= proxy
;
618 this.proxyModule
= proxy
;
622 for (ConstantField field
: constantFields
) {
623 field
.install(proxy
);
625 for (Iterator
<NamedInstaller
> iter
= staticInstallers
.values().iterator(); iter
.hasNext(); ) {
626 NamedInstaller installer
= iter
.next();
627 if (installer
.type
== NamedInstaller
.STATIC_METHOD
&& installer
.hasLocalMethod()) {
628 assignAliases((MethodInstaller
)installer
,staticAssignedNames
);
630 installer
.install(proxy
);
632 for (Iterator
<NamedInstaller
> iter
= instanceInstallers
.values().iterator(); iter
.hasNext(); ) {
633 NamedInstaller installer
= iter
.next();
634 if (installer
.type
== NamedInstaller
.INSTANCE_METHOD
&& installer
.hasLocalMethod()) {
635 assignAliases((MethodInstaller
)installer
,instanceAssignedNames
);
637 installer
.install(proxy
);
640 if (constructorInstaller
!= null) {
641 constructorInstaller
.install(proxy
);
644 // setup constants for public inner classes
645 Class
<?
>[] classes
= EMPTY_CLASS_ARRAY
;
647 classes
= javaClass
.getClasses();
648 } catch (SecurityException e
) {
650 for (int i
= classes
.length
; --i
>= 0; ) {
651 if (javaClass
== classes
[i
].getDeclaringClass()) {
652 Class
<?
> clazz
= classes
[i
];
653 String simpleName
= getSimpleName(clazz
);
655 if (simpleName
.length() == 0) continue;
657 // Ignore bad constant named inner classes pending JRUBY-697
658 if (IdUtil
.isConstant(simpleName
) && proxy
.getConstantAt(simpleName
) == null) {
659 proxy
.setConstant(simpleName
,
660 Java
.get_proxy_class(JAVA_UTILITIES
,get(getRuntime(),clazz
)));
664 // FIXME: bit of a kludge here (non-interface classes assigned to both
665 // class and module fields). simplifies proxy extender code, will go away
666 // when JI is overhauled (and proxy extenders are deprecated).
667 this.proxyClass
= proxy
;
668 this.proxyModule
= proxy
;
670 applyProxyExtenders();
672 // TODO: we can probably release our references to the constantFields
673 // array and static/instance callback hashes at this point.
676 private static void assignAliases(MethodInstaller installer
, Map
<String
, AssignedName
> assignedNames
) {
677 String name
= installer
.name
;
678 String rubyCasedName
= JavaUtil
.getRubyCasedName(name
);
679 addUnassignedAlias(rubyCasedName
,assignedNames
,installer
);
681 String javaPropertyName
= JavaUtil
.getJavaPropertyName(name
);
682 String rubyPropertyName
= null;
684 for (Method method
: installer
.methods
) {
685 Class
<?
>[] argTypes
= method
.getParameterTypes();
686 Class
<?
> resultType
= method
.getReturnType();
687 int argCount
= argTypes
.length
;
689 // Add property name aliases
690 if (javaPropertyName
!= null) {
691 if (rubyCasedName
.startsWith("get_")) {
692 rubyPropertyName
= rubyCasedName
.substring(4);
693 if (argCount
== 0 || // getFoo => foo
694 argCount
== 1 && argTypes
[0] == int.class) { // getFoo(int) => foo(int)
696 addUnassignedAlias(javaPropertyName
,assignedNames
,installer
);
697 addUnassignedAlias(rubyPropertyName
,assignedNames
,installer
);
699 } else if (rubyCasedName
.startsWith("set_")) {
700 rubyPropertyName
= rubyCasedName
.substring(4);
701 if (argCount
== 1 && resultType
== void.class) { // setFoo(Foo) => foo=(Foo)
702 addUnassignedAlias(javaPropertyName
+'=',assignedNames
,installer
);
703 addUnassignedAlias(rubyPropertyName
+'=',assignedNames
,installer
);
705 } else if (rubyCasedName
.startsWith("is_")) {
706 rubyPropertyName
= rubyCasedName
.substring(3);
707 if (resultType
== boolean.class) { // isFoo() => foo, isFoo(*) => foo(*)
708 addUnassignedAlias(javaPropertyName
,assignedNames
,installer
);
709 addUnassignedAlias(rubyPropertyName
,assignedNames
,installer
);
714 // Additionally add ?-postfixed aliases to any boolean methods and properties.
715 if (resultType
== boolean.class) {
716 // is_something?, contains_thing?
717 addUnassignedAlias(rubyCasedName
+'?',assignedNames
,installer
);
718 if (rubyPropertyName
!= null) {
720 addUnassignedAlias(rubyPropertyName
+'?',assignedNames
,installer
);
726 private static void addUnassignedAlias(String name
, Map
<String
, AssignedName
> assignedNames
,
727 MethodInstaller installer
) {
729 AssignedName assignedName
= (AssignedName
)assignedNames
.get(name
);
730 if (assignedName
== null) {
731 installer
.addAlias(name
);
732 assignedNames
.put(name
,new AssignedName(name
,AssignedName
.ALIAS
));
733 } else if (assignedName
.type
== AssignedName
.ALIAS
) {
734 installer
.addAlias(name
);
735 } else if (assignedName
.type
> AssignedName
.ALIAS
) {
736 // TODO: there will be some additional logic in this branch
737 // dealing with conflicting protected fields.
738 installer
.addAlias(name
);
739 assignedNames
.put(name
,new AssignedName(name
,AssignedName
.ALIAS
));
744 // old (quasi-deprecated) interface class
745 private void setupInterfaceProxy(final RubyClass proxy
) {
746 assert javaClass().isInterface();
747 assert proxyLock
.isHeldByCurrentThread();
748 assert this.proxyClass
== null;
749 this.proxyClass
= proxy
;
750 // nothing else to here - the module version will be
751 // included in the class.
754 public void setupInterfaceModule(final RubyModule module
) {
755 assert javaClass().isInterface();
756 assert proxyLock
.isHeldByCurrentThread();
757 assert this.proxyModule
== null;
758 this.unfinishedProxyModule
= module
;
759 Class
<?
> javaClass
= javaClass();
760 for (ConstantField field
: constantFields
) {
761 field
.install(module
);
763 // setup constants for public inner classes
764 Class
<?
>[] classes
= EMPTY_CLASS_ARRAY
;
766 classes
= javaClass
.getClasses();
767 } catch (SecurityException e
) {
769 for (int i
= classes
.length
; --i
>= 0; ) {
770 if (javaClass
== classes
[i
].getDeclaringClass()) {
771 Class
<?
> clazz
= classes
[i
];
772 String simpleName
= getSimpleName(clazz
);
773 if (simpleName
.length() == 0) continue;
775 // Ignore bad constant named inner classes pending JRUBY-697
776 if (IdUtil
.isConstant(simpleName
) && module
.getConstantAt(simpleName
) == null) {
777 module
.const_set(getRuntime().newString(simpleName
),
778 Java
.get_proxy_class(JAVA_UTILITIES
,get(getRuntime(),clazz
)));
783 this.proxyModule
= module
;
784 applyProxyExtenders();
787 public void addProxyExtender(final IRubyObject extender
) {
790 if (!extender
.respondsTo("extend_proxy")) {
791 throw getRuntime().newTypeError("proxy extender must have an extend_proxy method");
793 if (proxyModule
== null) {
794 if (proxyExtenders
== null) {
795 proxyExtenders
= new ArrayList
<IRubyObject
>();
797 proxyExtenders
.add(extender
);
799 getRuntime().getWarnings().warn(ID
.PROXY_EXTENDED_LATE
, " proxy extender added after proxy class created for " + this);
800 extendProxy(extender
);
807 private void applyProxyExtenders() {
808 ArrayList
<IRubyObject
> extenders
;
809 if ((extenders
= proxyExtenders
) != null) {
810 for (IRubyObject extender
: extenders
) {
811 extendProxy(extender
);
813 proxyExtenders
= null;
817 private void extendProxy(IRubyObject extender
) {
818 extender
.callMethod(getRuntime().getCurrentContext(), "extend_proxy", proxyModule
);
821 @JRubyMethod(required
= 1)
822 public IRubyObject
extend_proxy(IRubyObject extender
) {
823 addProxyExtender(extender
);
824 return getRuntime().getNil();
827 public static JavaClass
get(Ruby runtime
, Class
<?
> klass
) {
828 JavaClass javaClass
= runtime
.getJavaSupport().getJavaClassFromCache(klass
);
829 if (javaClass
== null) {
830 javaClass
= createJavaClass(runtime
,klass
);
835 public static RubyArray
getRubyArray(Ruby runtime
, Class
<?
>[] classes
) {
836 IRubyObject
[] javaClasses
= new IRubyObject
[classes
.length
];
837 for (int i
= classes
.length
; --i
>= 0; ) {
838 javaClasses
[i
] = get(runtime
, classes
[i
]);
840 return runtime
.newArrayNoCopy(javaClasses
);
843 private static synchronized JavaClass
createJavaClass(Ruby runtime
, Class
<?
> klass
) {
844 // double-check the cache now that we're synchronized
845 JavaClass javaClass
= runtime
.getJavaSupport().getJavaClassFromCache(klass
);
846 if (javaClass
== null) {
847 javaClass
= new JavaClass(runtime
, klass
);
848 runtime
.getJavaSupport().putJavaClassIntoCache(javaClass
);
853 public static RubyClass
createJavaClassClass(Ruby runtime
, RubyModule javaModule
) {
854 // FIXME: Determine if a real allocator is needed here. Do people want to extend
855 // JavaClass? Do we want them to do that? Can you Class.new(JavaClass)? Should
857 // TODO: NOT_ALLOCATABLE_ALLOCATOR is probably ok here, since we don't intend for people to monkey with
858 // this type and it can't be marshalled. Confirm. JRUBY-415
859 RubyClass result
= javaModule
.defineClassUnder("JavaClass", javaModule
.fastGetClass("JavaObject"), ObjectAllocator
.NOT_ALLOCATABLE_ALLOCATOR
);
861 result
.includeModule(runtime
.fastGetModule("Comparable"));
863 result
.defineAnnotatedMethods(JavaClass
.class);
865 result
.getMetaClass().undefineMethod("new");
866 result
.getMetaClass().undefineMethod("allocate");
871 public static synchronized JavaClass
forNameVerbose(Ruby runtime
, String className
) {
872 Class
<?
> klass
= runtime
.getJavaSupport().loadJavaClassVerbose(className
);
873 return JavaClass
.get(runtime
, klass
);
876 public static synchronized JavaClass
forNameQuiet(Ruby runtime
, String className
) {
877 Class klass
= runtime
.getJavaSupport().loadJavaClassQuiet(className
);
878 return JavaClass
.get(runtime
, klass
);
881 @JRubyMethod(name
= "for_name", required
= 1, meta
= true)
882 public static JavaClass
for_name(IRubyObject recv
, IRubyObject name
) {
883 return forNameVerbose(recv
.getRuntime(), name
.asJavaString());
886 private static final Callback __jsend_method
= new Callback() {
887 public IRubyObject
execute(IRubyObject self
, IRubyObject
[] args
, Block block
) {
888 String name
= args
[0].asJavaString();
890 DynamicMethod method
= self
.getMetaClass().searchMethod(name
);
891 int v
= method
.getArity().getValue();
893 IRubyObject
[] newArgs
= new IRubyObject
[args
.length
- 1];
894 System
.arraycopy(args
, 1, newArgs
, 0, newArgs
.length
);
896 if(v
< 0 || v
== (newArgs
.length
)) {
897 return RuntimeHelpers
.invoke(self
.getRuntime().getCurrentContext(), self
, name
, newArgs
, block
);
899 RubyClass superClass
= self
.getMetaClass().getSuperClass();
900 return RuntimeHelpers
.invokeAs(self
.getRuntime().getCurrentContext(), superClass
, self
, name
, newArgs
, CallType
.SUPER
, block
);
904 public Arity
getArity() {
905 return Arity
.optional();
910 public RubyModule
ruby_class() {
911 // Java.getProxyClass deals with sync issues, so we won't duplicate the logic here
912 return Java
.getProxyClass(getRuntime(), this);
915 @JRubyMethod(name
= "public?")
916 public RubyBoolean
public_p() {
917 return getRuntime().newBoolean(Modifier
.isPublic(javaClass().getModifiers()));
920 @JRubyMethod(name
= "protected?")
921 public RubyBoolean
protected_p() {
922 return getRuntime().newBoolean(Modifier
.isProtected(javaClass().getModifiers()));
925 @JRubyMethod(name
= "private?")
926 public RubyBoolean
private_p() {
927 return getRuntime().newBoolean(Modifier
.isPrivate(javaClass().getModifiers()));
930 public Class
javaClass() {
931 return (Class
) getValue();
934 @JRubyMethod(name
= "final?")
935 public RubyBoolean
final_p() {
936 return getRuntime().newBoolean(Modifier
.isFinal(javaClass().getModifiers()));
939 @JRubyMethod(name
= "interface?")
940 public RubyBoolean
interface_p() {
941 return getRuntime().newBoolean(javaClass().isInterface());
944 @JRubyMethod(name
= "array?")
945 public RubyBoolean
array_p() {
946 return getRuntime().newBoolean(javaClass().isArray());
949 @JRubyMethod(name
= "enum?")
950 public RubyBoolean
enum_p() {
951 return getRuntime().newBoolean(javaClass().isEnum());
954 @JRubyMethod(name
= "annotation?")
955 public RubyBoolean
annotation_p() {
956 return getRuntime().newBoolean(javaClass().isAnnotation());
959 @JRubyMethod(name
= "anonymous_class?")
960 public RubyBoolean
anonymous_class_p() {
961 return getRuntime().newBoolean(javaClass().isAnonymousClass());
964 @JRubyMethod(name
= "local_class?")
965 public RubyBoolean
local_class_p() {
966 return getRuntime().newBoolean(javaClass().isLocalClass());
969 @JRubyMethod(name
= "member_class?")
970 public RubyBoolean
member_class_p() {
971 return getRuntime().newBoolean(javaClass().isMemberClass());
974 @JRubyMethod(name
= "synthetic?")
975 public IRubyObject
synthetic_p() {
976 return getRuntime().newBoolean(javaClass().isSynthetic());
979 @JRubyMethod(name
= {"name", "to_s"})
980 public RubyString
name() {
981 return getRuntime().newString(javaClass().getName());
985 public IRubyObject
canonical_name() {
986 String canonicalName
= javaClass().getCanonicalName();
987 if (canonicalName
!= null) {
988 return getRuntime().newString(canonicalName
);
990 return getRuntime().getNil();
993 @JRubyMethod(name
= "package")
994 public IRubyObject
get_package() {
995 return Java
.getInstance(getRuntime(), javaClass().getPackage());
999 public IRubyObject
class_loader() {
1000 return Java
.getInstance(getRuntime(), javaClass().getClassLoader());
1004 public IRubyObject
protection_domain() {
1005 return Java
.getInstance(getRuntime(), javaClass().getProtectionDomain());
1008 @JRubyMethod(required
= 1)
1009 public IRubyObject
resource(IRubyObject name
) {
1010 return Java
.getInstance(getRuntime(), javaClass().getResource(name
.asJavaString()));
1013 @JRubyMethod(required
= 1)
1014 public IRubyObject
resource_as_stream(IRubyObject name
) {
1015 return Java
.getInstance(getRuntime(), javaClass().getResourceAsStream(name
.asJavaString()));
1018 @JRubyMethod(required
= 1)
1019 public IRubyObject
resource_as_string(IRubyObject name
) {
1020 InputStream in
= javaClass().getResourceAsStream(name
.asJavaString());
1021 if (in
== null) return getRuntime().getNil();
1022 ByteArrayOutputStream out
= new ByteArrayOutputStream();
1025 byte[] buf
= new byte[4096];
1026 while ((len
= in
.read(buf
)) >= 0) {
1027 out
.write(buf
, 0, len
);
1029 } catch (IOException e
) {
1030 throw getRuntime().newIOErrorFromException(e
);
1032 return getRuntime().newString(new ByteList(out
.toByteArray(), false));
1035 @SuppressWarnings("unchecked")
1036 @JRubyMethod(required
= 1)
1037 public IRubyObject
annotation(IRubyObject annoClass
) {
1038 if (!(annoClass
instanceof JavaClass
)) {
1039 throw getRuntime().newTypeError(annoClass
, getRuntime().getJavaSupport().getJavaClassClass());
1041 return Java
.getInstance(getRuntime(), javaClass().getAnnotation(((JavaClass
)annoClass
).javaClass()));
1045 public IRubyObject
annotations() {
1046 // note: intentionally returning the actual array returned from Java, rather
1047 // than wrapping it in a RubyArray. wave of the future, when java_class will
1048 // return the actual class, rather than a JavaClass wrapper.
1049 return Java
.getInstance(getRuntime(), javaClass().getAnnotations());
1052 @JRubyMethod(name
= "annotations?")
1053 public RubyBoolean
annotations_p() {
1054 return getRuntime().newBoolean(javaClass().getAnnotations().length
> 0);
1058 public IRubyObject
declared_annotations() {
1059 // see note above re: return type
1060 return Java
.getInstance(getRuntime(), javaClass().getDeclaredAnnotations());
1063 @JRubyMethod(name
= "declared_annotations?")
1064 public RubyBoolean
declared_annotations_p() {
1065 return getRuntime().newBoolean(javaClass().getDeclaredAnnotations().length
> 0);
1068 @SuppressWarnings("unchecked")
1069 @JRubyMethod(name
= "annotation_present?", required
= 1)
1070 public IRubyObject
annotation_present_p(IRubyObject annoClass
) {
1071 if (!(annoClass
instanceof JavaClass
)) {
1072 throw getRuntime().newTypeError(annoClass
, getRuntime().getJavaSupport().getJavaClassClass());
1074 return getRuntime().newBoolean(javaClass().isAnnotationPresent(((JavaClass
)annoClass
).javaClass()));
1078 public IRubyObject
modifiers() {
1079 return getRuntime().newFixnum(javaClass().getModifiers());
1083 public IRubyObject
declaring_class() {
1084 Class
<?
> clazz
= javaClass().getDeclaringClass();
1085 if (clazz
!= null) {
1086 return JavaClass
.get(getRuntime(), clazz
);
1088 return getRuntime().getNil();
1092 public IRubyObject
enclosing_class() {
1093 return Java
.getInstance(getRuntime(), javaClass().getEnclosingClass());
1097 public IRubyObject
enclosing_constructor() {
1098 Constructor
<?
> ctor
= javaClass().getEnclosingConstructor();
1100 return new JavaConstructor(getRuntime(), ctor
);
1102 return getRuntime().getNil();
1106 public IRubyObject
enclosing_method() {
1107 Method meth
= javaClass().getEnclosingMethod();
1109 return new JavaMethod(getRuntime(), meth
);
1111 return getRuntime().getNil();
1115 public IRubyObject
enum_constants() {
1116 return Java
.getInstance(getRuntime(), javaClass().getEnumConstants());
1120 public IRubyObject
generic_interfaces() {
1121 return Java
.getInstance(getRuntime(), javaClass().getGenericInterfaces());
1125 public IRubyObject
generic_superclass() {
1126 return Java
.getInstance(getRuntime(), javaClass().getGenericSuperclass());
1130 public IRubyObject
type_parameters() {
1131 return Java
.getInstance(getRuntime(), javaClass().getTypeParameters());
1135 public IRubyObject
signers() {
1136 return Java
.getInstance(getRuntime(), javaClass().getSigners());
1139 private static String
getSimpleName(Class
<?
> clazz
) {
1140 if (clazz
.isArray()) {
1141 return getSimpleName(clazz
.getComponentType()) + "[]";
1144 String className
= clazz
.getName();
1145 int len
= className
.length();
1146 int i
= className
.lastIndexOf('$');
1150 } while (i
< len
&& Character
.isDigit(className
.charAt(i
)));
1151 return className
.substring(i
);
1154 return className
.substring(className
.lastIndexOf('.') + 1);
1158 public RubyString
simple_name() {
1159 return getRuntime().newString(getSimpleName(javaClass()));
1163 public IRubyObject
superclass() {
1164 Class
<?
> superclass
= javaClass().getSuperclass();
1165 if (superclass
== null) {
1166 return getRuntime().getNil();
1168 return JavaClass
.get(getRuntime(), superclass
);
1171 @JRubyMethod(name
= "<=>", required
= 1)
1172 public RubyFixnum
op_cmp(IRubyObject other
) {
1173 if (! (other
instanceof JavaClass
)) {
1174 throw getRuntime().newTypeError("<=> requires JavaClass (" + other
.getType() + " given)");
1176 JavaClass otherClass
= (JavaClass
) other
;
1177 if (this.javaClass() == otherClass
.javaClass()) {
1178 return getRuntime().newFixnum(0);
1180 if (otherClass
.javaClass().isAssignableFrom(this.javaClass())) {
1181 return getRuntime().newFixnum(-1);
1183 return getRuntime().newFixnum(1);
1187 public RubyArray
java_instance_methods() {
1188 return java_methods(javaClass().getMethods(), false);
1192 public RubyArray
declared_instance_methods() {
1193 return java_methods(javaClass().getDeclaredMethods(), false);
1196 private RubyArray
java_methods(Method
[] methods
, boolean isStatic
) {
1197 RubyArray result
= getRuntime().newArray(methods
.length
);
1198 for (int i
= 0; i
< methods
.length
; i
++) {
1199 Method method
= methods
[i
];
1200 if (isStatic
== Modifier
.isStatic(method
.getModifiers())) {
1201 result
.append(JavaMethod
.create(getRuntime(), method
));
1208 public RubyArray
java_class_methods() {
1209 return java_methods(javaClass().getMethods(), true);
1213 public RubyArray
declared_class_methods() {
1214 return java_methods(javaClass().getDeclaredMethods(), true);
1217 @JRubyMethod(required
= 1, rest
= true)
1218 public JavaMethod
java_method(IRubyObject
[] args
) throws ClassNotFoundException
{
1219 String methodName
= args
[0].asJavaString();
1220 Class
<?
>[] argumentTypes
= buildArgumentTypes(args
);
1221 return JavaMethod
.create(getRuntime(), javaClass(), methodName
, argumentTypes
);
1224 @JRubyMethod(required
= 1, rest
= true)
1225 public JavaMethod
declared_method(IRubyObject
[] args
) throws ClassNotFoundException
{
1226 String methodName
= args
[0].asJavaString();
1227 Class
<?
>[] argumentTypes
= buildArgumentTypes(args
);
1228 return JavaMethod
.createDeclared(getRuntime(), javaClass(), methodName
, argumentTypes
);
1231 @JRubyMethod(required
= 1, rest
= true)
1232 public JavaCallable
declared_method_smart(IRubyObject
[] args
) throws ClassNotFoundException
{
1233 String methodName
= args
[0].asJavaString();
1234 Class
<?
>[] argumentTypes
= buildArgumentTypes(args
);
1236 JavaCallable callable
= getMatchingCallable(getRuntime(), javaClass(), methodName
, argumentTypes
);
1238 if (callable
!= null) return callable
;
1240 throw getRuntime().newNameError("undefined method '" + methodName
+ "' for class '" + javaClass().getName() + "'",
1244 public static JavaCallable
getMatchingCallable(Ruby runtime
, Class
<?
> javaClass
, String methodName
, Class
<?
>[] argumentTypes
) {
1245 if ("<init>".equals(methodName
)) {
1246 return JavaConstructor
.getMatchingConstructor(runtime
, javaClass
, argumentTypes
);
1248 // FIXME: do we really want 'declared' methods? includes private/protected, and does _not_
1249 // include superclass methods
1250 return JavaMethod
.getMatchingDeclaredMethod(runtime
, javaClass
, methodName
, argumentTypes
);
1254 private Class
<?
>[] buildArgumentTypes(IRubyObject
[] args
) throws ClassNotFoundException
{
1255 if (args
.length
< 1) {
1256 throw getRuntime().newArgumentError(args
.length
, 1);
1258 Class
<?
>[] argumentTypes
= new Class
[args
.length
- 1];
1259 for (int i
= 1; i
< args
.length
; i
++) {
1261 if (args
[i
] instanceof JavaClass
) {
1262 type
= (JavaClass
)args
[i
];
1263 } else if (args
[i
].respondsTo("java_class")) {
1264 type
= (JavaClass
)args
[i
].callMethod(getRuntime().getCurrentContext(), "java_class");
1266 type
= for_name(this, args
[i
]);
1268 argumentTypes
[i
- 1] = type
.javaClass();
1270 return argumentTypes
;
1274 public RubyArray
constructors() {
1276 if ((ctors
= constructors
) != null) return ctors
;
1277 return constructors
= buildConstructors(javaClass().getConstructors());
1281 public RubyArray
classes() {
1282 return JavaClass
.getRubyArray(getRuntime(), javaClass().getClasses());
1286 public RubyArray
declared_classes() {
1287 Ruby runtime
= getRuntime();
1288 RubyArray result
= runtime
.newArray();
1289 Class
<?
> javaClass
= javaClass();
1291 Class
<?
>[] classes
= javaClass
.getDeclaredClasses();
1292 for (int i
= 0; i
< classes
.length
; i
++) {
1293 if (Modifier
.isPublic(classes
[i
].getModifiers())) {
1294 result
.append(get(runtime
, classes
[i
]));
1297 } catch (SecurityException e
) {
1298 // restrictive security policy; no matter, we only want public
1301 Class
<?
>[] classes
= javaClass
.getClasses();
1302 for (int i
= 0; i
< classes
.length
; i
++) {
1303 if (javaClass
== classes
[i
].getDeclaringClass()) {
1304 result
.append(get(runtime
, classes
[i
]));
1307 } catch (SecurityException e2
) {
1308 // very restrictive policy (disallows Member.PUBLIC)
1309 // we'd never actually get this far in that case
1316 public RubyArray
declared_constructors() {
1317 return buildConstructors(javaClass().getDeclaredConstructors());
1320 private RubyArray
buildConstructors(Constructor
<?
>[] constructors
) {
1321 RubyArray result
= getRuntime().newArray(constructors
.length
);
1322 for (int i
= 0; i
< constructors
.length
; i
++) {
1323 result
.append(new JavaConstructor(getRuntime(), constructors
[i
]));
1328 @JRubyMethod(rest
= true)
1329 public JavaConstructor
constructor(IRubyObject
[] args
) {
1331 Class
<?
>[] parameterTypes
= buildClassArgs(args
);
1332 Constructor
<?
> constructor
= javaClass().getConstructor(parameterTypes
);
1333 return new JavaConstructor(getRuntime(), constructor
);
1334 } catch (NoSuchMethodException nsme
) {
1335 throw getRuntime().newNameError("no matching java constructor", null);
1339 @JRubyMethod(rest
= true)
1340 public JavaConstructor
declared_constructor(IRubyObject
[] args
) {
1342 Class
<?
>[] parameterTypes
= buildClassArgs(args
);
1343 Constructor
<?
> constructor
= javaClass().getDeclaredConstructor (parameterTypes
);
1344 return new JavaConstructor(getRuntime(), constructor
);
1345 } catch (NoSuchMethodException nsme
) {
1346 throw getRuntime().newNameError("no matching java constructor", null);
1350 private Class
<?
>[] buildClassArgs(IRubyObject
[] args
) {
1351 JavaSupport javaSupport
= getRuntime().getJavaSupport();
1352 Class
<?
>[] parameterTypes
= new Class
<?
>[args
.length
];
1353 for (int i
= args
.length
; --i
>= 0; ) {
1354 String name
= args
[i
].asJavaString();
1355 parameterTypes
[i
] = javaSupport
.loadJavaClassVerbose(name
);
1357 return parameterTypes
;
1361 public JavaClass
array_class() {
1362 return JavaClass
.get(getRuntime(), Array
.newInstance(javaClass(), 0).getClass());
1365 @JRubyMethod(required
= 1)
1366 public JavaObject
new_array(IRubyObject lengthArgument
) {
1367 if (lengthArgument
instanceof RubyInteger
) {
1368 // one-dimensional array
1369 int length
= (int) ((RubyInteger
) lengthArgument
).getLongValue();
1370 return new JavaArray(getRuntime(), Array
.newInstance(javaClass(), length
));
1371 } else if (lengthArgument
instanceof RubyArray
) {
1372 // n-dimensional array
1373 List list
= ((RubyArray
)lengthArgument
).getList();
1374 int length
= list
.size();
1376 throw getRuntime().newArgumentError("empty dimensions specifier for java array");
1378 int[] dimensions
= new int[length
];
1379 for (int i
= length
; --i
>= 0; ) {
1380 IRubyObject dimensionLength
= (IRubyObject
)list
.get(i
);
1381 if ( !(dimensionLength
instanceof RubyInteger
) ) {
1383 .newTypeError(dimensionLength
, getRuntime().getInteger());
1385 dimensions
[i
] = (int) ((RubyInteger
) dimensionLength
).getLongValue();
1387 return new JavaArray(getRuntime(), Array
.newInstance(javaClass(), dimensions
));
1389 throw getRuntime().newArgumentError(
1390 "invalid length or dimensions specifier for java array" +
1391 " - must be Integer or Array of Integer");
1395 public IRubyObject
emptyJavaArray(ThreadContext context
) {
1396 JavaArray javaArray
= new JavaArray(getRuntime(), Array
.newInstance(javaClass(), 0));
1397 RubyClass proxyClass
= (RubyClass
)Java
.get_proxy_class(javaArray
, array_class());
1399 ArrayJavaProxy proxy
= new ArrayJavaProxy(context
.getRuntime(), proxyClass
);
1400 proxy
.dataWrapStruct(javaArray
);
1405 public IRubyObject
javaArraySubarray(ThreadContext context
, JavaArray fromArray
, int index
, int size
) {
1406 int actualLength
= Array
.getLength(fromArray
.getValue());
1407 if (index
>= actualLength
) {
1408 return context
.getRuntime().getNil();
1410 if (index
+ size
> actualLength
) {
1411 size
= actualLength
- index
;
1414 Object newArray
= Array
.newInstance(javaClass(), size
);
1415 JavaArray javaArray
= new JavaArray(getRuntime(), newArray
);
1416 System
.arraycopy(fromArray
.getValue(), index
, newArray
, 0, size
);
1417 RubyClass proxyClass
= (RubyClass
)Java
.get_proxy_class(javaArray
, array_class());
1419 ArrayJavaProxy proxy
= new ArrayJavaProxy(context
.getRuntime(), proxyClass
);
1420 proxy
.dataWrapStruct(javaArray
);
1427 * Contatenate two Java arrays into a new one. The component type of the
1428 * additional array must be assignable to the component type of the
1436 public IRubyObject
concatArrays(ThreadContext context
, JavaArray original
, JavaArray additional
) {
1437 int oldLength
= (int)original
.length().getLongValue();
1438 int addLength
= (int)additional
.length().getLongValue();
1439 Object newArray
= Array
.newInstance(javaClass(), oldLength
+ addLength
);
1440 JavaArray javaArray
= new JavaArray(getRuntime(), newArray
);
1441 System
.arraycopy(original
.getValue(), 0, newArray
, 0, oldLength
);
1442 System
.arraycopy(additional
.getValue(), 0, newArray
, oldLength
, addLength
);
1443 RubyClass proxyClass
= (RubyClass
)Java
.get_proxy_class(javaArray
, array_class());
1445 ArrayJavaProxy proxy
= new ArrayJavaProxy(context
.getRuntime(), proxyClass
);
1446 proxy
.dataWrapStruct(javaArray
);
1452 * The slow version for when concatenating a Java array of a different type.
1459 public IRubyObject
concatArrays(ThreadContext context
, JavaArray original
, IRubyObject additional
) {
1460 int oldLength
= (int)original
.length().getLongValue();
1461 int addLength
= (int)((RubyFixnum
)RuntimeHelpers
.invoke(context
, additional
, "length")).getLongValue();
1462 Object newArray
= Array
.newInstance(javaClass(), oldLength
+ addLength
);
1463 JavaArray javaArray
= new JavaArray(getRuntime(), newArray
);
1464 System
.arraycopy(original
.getValue(), 0, newArray
, 0, oldLength
);
1465 RubyClass proxyClass
= (RubyClass
)Java
.get_proxy_class(javaArray
, array_class());
1466 ArrayJavaProxy proxy
= new ArrayJavaProxy(context
.getRuntime(), proxyClass
);
1467 proxy
.dataWrapStruct(javaArray
);
1469 Ruby runtime
= context
.getRuntime();
1470 for (int i
= 0; i
< addLength
; i
++) {
1471 RuntimeHelpers
.invoke(context
, proxy
, "[]=", runtime
.newFixnum(oldLength
+ i
),
1472 RuntimeHelpers
.invoke(context
, additional
, "[]", runtime
.newFixnum(i
)));
1478 public IRubyObject
javaArrayFromRubyArray(ThreadContext context
, IRubyObject fromArray
) {
1479 Ruby runtime
= context
.getRuntime();
1480 if (!(fromArray
instanceof RubyArray
)) {
1481 throw runtime
.newTypeError(fromArray
, runtime
.getArray());
1483 RubyArray rubyArray
= (RubyArray
)fromArray
;
1484 JavaArray javaArray
= new JavaArray(getRuntime(), Array
.newInstance(javaClass(), rubyArray
.size()));
1485 ArrayJavaAddons
.copyDataToJavaArray(context
, rubyArray
, javaArray
);
1486 RubyClass proxyClass
= (RubyClass
)Java
.get_proxy_class(javaArray
, array_class());
1488 ArrayJavaProxy proxy
= new ArrayJavaProxy(runtime
, proxyClass
);
1489 proxy
.dataWrapStruct(javaArray
);
1495 public RubyArray
fields() {
1496 return buildFieldResults(javaClass().getFields());
1500 public RubyArray
declared_fields() {
1501 return buildFieldResults(javaClass().getDeclaredFields());
1504 private RubyArray
buildFieldResults(Field
[] fields
) {
1505 RubyArray result
= getRuntime().newArray(fields
.length
);
1506 for (int i
= 0; i
< fields
.length
; i
++) {
1507 result
.append(new JavaField(getRuntime(), fields
[i
]));
1512 @JRubyMethod(required
= 1)
1513 public JavaField
field(IRubyObject name
) {
1514 String stringName
= name
.asJavaString();
1516 Field field
= javaClass().getField(stringName
);
1517 return new JavaField(getRuntime(), field
);
1518 } catch (NoSuchFieldException nsfe
) {
1519 throw undefinedFieldError(stringName
);
1523 @JRubyMethod(required
= 1)
1524 public JavaField
declared_field(IRubyObject name
) {
1525 String stringName
= name
.asJavaString();
1527 Field field
= javaClass().getDeclaredField(stringName
);
1528 return new JavaField(getRuntime(), field
);
1529 } catch (NoSuchFieldException nsfe
) {
1530 throw undefinedFieldError(stringName
);
1534 private RaiseException
undefinedFieldError(String name
) {
1535 return getRuntime().newNameError("undefined field '" + name
+ "' for class '" + javaClass().getName() + "'", name
);
1539 public RubyArray
interfaces() {
1540 return JavaClass
.getRubyArray(getRuntime(), javaClass().getInterfaces());
1543 @JRubyMethod(name
= "primitive?")
1544 public RubyBoolean
primitive_p() {
1545 return getRuntime().newBoolean(isPrimitive());
1548 @JRubyMethod(name
= "assignable_from?", required
= 1)
1549 public RubyBoolean
assignable_from_p(IRubyObject other
) {
1550 if (! (other
instanceof JavaClass
)) {
1551 throw getRuntime().newTypeError("assignable_from requires JavaClass (" + other
.getType() + " given)");
1554 Class
<?
> otherClass
= ((JavaClass
) other
).javaClass();
1555 return assignable(javaClass(), otherClass
) ?
getRuntime().getTrue() : getRuntime().getFalse();
1558 static boolean assignable(Class
<?
> thisClass
, Class
<?
> otherClass
) {
1559 if(!thisClass
.isPrimitive() && otherClass
== Void
.TYPE
||
1560 thisClass
.isAssignableFrom(otherClass
)) {
1564 otherClass
= JavaUtil
.primitiveToWrapper(otherClass
);
1565 thisClass
= JavaUtil
.primitiveToWrapper(thisClass
);
1567 if(thisClass
.isAssignableFrom(otherClass
)) {
1570 if(Number
.class.isAssignableFrom(thisClass
)) {
1571 if(Number
.class.isAssignableFrom(otherClass
)) {
1574 if(otherClass
.equals(Character
.class)) {
1578 if(thisClass
.equals(Character
.class)) {
1579 if(Number
.class.isAssignableFrom(otherClass
)) {
1586 private boolean isPrimitive() {
1587 return javaClass().isPrimitive();
1591 public JavaClass
component_type() {
1592 if (! javaClass().isArray()) {
1593 throw getRuntime().newTypeError("not a java array-class");
1595 return JavaClass
.get(getRuntime(), javaClass().getComponentType());