ConcreteJavaProxy.java

  1. package org.jruby.java.proxies;

  2. import org.jruby.Ruby;
  3. import org.jruby.RubyClass;
  4. import org.jruby.RubyModule;
  5. import org.jruby.internal.runtime.methods.DynamicMethod;
  6. import org.jruby.javasupport.Java;
  7. import org.jruby.runtime.Block;
  8. import org.jruby.runtime.CallSite;
  9. import org.jruby.runtime.MethodIndex;
  10. import org.jruby.runtime.ObjectAllocator;
  11. import org.jruby.runtime.ThreadContext;
  12. import org.jruby.runtime.Visibility;
  13. import org.jruby.runtime.builtin.IRubyObject;

  14. public class ConcreteJavaProxy extends JavaProxy {
  15.     public ConcreteJavaProxy(Ruby runtime, RubyClass klazz) {
  16.         super(runtime, klazz);
  17.     }
  18.    
  19.     public ConcreteJavaProxy(Ruby runtime, RubyClass klazz, Object object) {
  20.         super(runtime, klazz, object);
  21.     }
  22.    
  23.     public static RubyClass createConcreteJavaProxy(ThreadContext context) {
  24.         Ruby runtime = context.runtime;
  25.        
  26.         RubyClass concreteJavaProxy = runtime.defineClass("ConcreteJavaProxy",
  27.                 runtime.getJavaSupport().getJavaProxyClass(),
  28.                 new ObjectAllocator() {
  29.             public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
  30.                 return new ConcreteJavaProxy(runtime, klazz);
  31.             }
  32.         });
  33.         initialize(concreteJavaProxy);
  34.         return concreteJavaProxy;
  35.     }

  36.     protected static void initialize(RubyClass concreteJavaProxy) {
  37.         concreteJavaProxy.addMethod("initialize", new org.jruby.internal.runtime.methods.JavaMethod(concreteJavaProxy, Visibility.PRIVATE) {
  38.             private final CallSite jcreateSite = MethodIndex.getFunctionalCallSite("__jcreate!");
  39.             @Override
  40.             public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
  41.                 return jcreateSite.call(context, self, self, args, block);
  42.             }
  43.             @Override
  44.             public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
  45.                 return jcreateSite.call(context, self, self, block);
  46.             }
  47.             @Override
  48.             public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, Block block) {
  49.                 return jcreateSite.call(context, self, self, arg0, block);
  50.             }
  51.             @Override
  52.             public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, Block block) {
  53.                 return jcreateSite.call(context, self, self, arg0, arg1, block);
  54.             }
  55.             @Override
  56.             public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
  57.                 return jcreateSite.call(context, self, self, arg0, arg1, arg2, block);
  58.             }
  59.             @Override
  60.             public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args) {
  61.                 return jcreateSite.call(context, self, self, args);
  62.             }
  63.             @Override
  64.             public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name) {
  65.                 return jcreateSite.call(context, self, self);
  66.             }
  67.             @Override
  68.             public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0) {
  69.                 return jcreateSite.call(context, self, self, arg0);
  70.             }
  71.             @Override
  72.             public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1) {
  73.                 return jcreateSite.call(context, self, self, arg0, arg1);
  74.             }
  75.             @Override
  76.             public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
  77.                 return jcreateSite.call(context, self, self, arg0, arg1, arg2);
  78.             }
  79.         });

  80.         // We define a custom "new" method to ensure that __jcreate! is getting called,
  81.         // so that if the user doesn't call super in their subclasses, the object will
  82.         // still get set up properly. See JRUBY-4704.
  83.         RubyClass singleton = concreteJavaProxy.getSingletonClass();
  84.         final DynamicMethod oldNew = singleton.searchMethod("new");
  85.         singleton.addMethod("new", new org.jruby.internal.runtime.methods.JavaMethod(singleton, Visibility.PUBLIC) {
  86.             private final CallSite jcreateSite = MethodIndex.getFunctionalCallSite("__jcreate!");

  87.             private boolean needsCreate(IRubyObject recv) {
  88.                 return ((JavaProxy)recv).object == null;
  89.             }

  90.             @Override
  91.             public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
  92.                 IRubyObject proxy = oldNew.call(context, self, clazz, "new_proxy", args, block);
  93.                 if (needsCreate(proxy)) jcreateSite.call(context, proxy, proxy, args, block);
  94.                 return proxy;
  95.             }

  96.             @Override
  97.             public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
  98.                 IRubyObject proxy = oldNew.call(context, self, clazz, "new_proxy", block);
  99.                 if (needsCreate(proxy)) jcreateSite.call(context, proxy, proxy, block);
  100.                 return proxy;
  101.             }

  102.             @Override
  103.             public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, Block block) {
  104.                 IRubyObject proxy = oldNew.call(context, self, clazz, "new_proxy", arg0, block);
  105.                 if (needsCreate(proxy)) jcreateSite.call(context, proxy, proxy, arg0, block);

  106.                 return proxy;
  107.             }

  108.             @Override
  109.             public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, Block block) {
  110.                 IRubyObject proxy = oldNew.call(context, self, clazz, "new_proxy", arg0, arg1, block);
  111.                 if (needsCreate(proxy)) jcreateSite.call(context, proxy, proxy, arg0, arg1, block);
  112.                 return proxy;
  113.             }

  114.             @Override
  115.             public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
  116.                 IRubyObject proxy = oldNew.call(context, self, clazz, "new_proxy", arg0, arg1, arg2, block);
  117.                 if (needsCreate(proxy)) jcreateSite.call(context, proxy, proxy, arg0, arg1, arg2, block);
  118.                 return proxy;
  119.             }

  120.             @Override
  121.             public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args) {
  122.                 IRubyObject proxy = oldNew.call(context, self, clazz, "new_proxy", args);
  123.                 if (needsCreate(proxy)) jcreateSite.call(context, proxy, proxy, args);
  124.                 return proxy;
  125.             }

  126.             @Override
  127.             public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name) {
  128.                 IRubyObject proxy = oldNew.call(context, self, clazz, "new_proxy");
  129.                 if (needsCreate(proxy)) jcreateSite.call(context, proxy, proxy);
  130.                 return proxy;
  131.             }

  132.             @Override
  133.             public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0) {
  134.                 IRubyObject proxy = oldNew.call(context, self, clazz, "new_proxy", arg0);
  135.                 if (needsCreate(proxy)) jcreateSite.call(context, proxy, proxy, arg0);
  136.                 return proxy;
  137.             }

  138.             @Override
  139.             public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1) {
  140.                 IRubyObject proxy = oldNew.call(context, self, clazz, "new_proxy", arg0, arg1);
  141.                 if (needsCreate(proxy)) jcreateSite.call(context, proxy, proxy, arg0, arg1);
  142.                 return proxy;
  143.             }

  144.             @Override
  145.             public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
  146.                 IRubyObject proxy = oldNew.call(context, self, clazz, "new_proxy", arg0, arg1, arg2);
  147.                 if (needsCreate(proxy)) jcreateSite.call(context, proxy, proxy, arg0, arg1, arg2);
  148.                 return proxy;
  149.             }
  150.         });
  151.     }

  152.     // This alternate ivar logic is disabled because it can cause self-referencing
  153.     // chains to keep the original object alive. See JRUBY-4832.
  154. //    @Override
  155. //    public Object getVariable(int index) {
  156. //        return getRuntime().getJavaSupport().getJavaObjectVariable(this, index);
  157. //    }
  158. //
  159. //    @Override
  160. //    public void setVariable(int index, Object value) {
  161. //        getRuntime().getJavaSupport().setJavaObjectVariable(this, index, value);
  162. //    }

  163.     /**
  164.      * Because we can't physically associate an ID with a Java object, we can
  165.      * only use the identity hashcode here.
  166.      *
  167.      * @return The identity hashcode for the Java object.
  168.      */
  169.     public IRubyObject id() {
  170.         return getRuntime().newFixnum(System.identityHashCode(getObject()));
  171.     }

  172.     @Override
  173.     public Object toJava(Class type) {
  174.         Object obj = getObject();
  175.         Class cls = obj.getClass();

  176.         if (type.isPrimitive()) {
  177.             if (type == Void.TYPE) return null;
  178.            
  179.             if (obj instanceof Number && type != Boolean.TYPE ||
  180.                     obj instanceof Character && type == Character.TYPE ||
  181.                     obj instanceof Boolean && type == Boolean.TYPE) {
  182.                 // FIXME in more permissive call paths, like invokedynamic, this can allow
  183.                 // precision-loading downcasts to happen silently
  184.                 return obj;
  185.             }
  186.         } else if (type.isAssignableFrom(cls)) {
  187.             if (Java.OBJECT_PROXY_CACHE || metaClass.getCacheProxy()) {
  188.                 getRuntime().getJavaSupport().getObjectProxyCache().put(obj, this);
  189.             }
  190.             return obj;
  191.         }
  192.        
  193.         throw getRuntime().newTypeError("failed to coerce " + cls.getName() + " to " + type.getName());
  194.     }
  195. }