0

I use ASM library to generate bytecodes and load them using Unsafe.defineAnonymous as a Class. Both work in most of cases, but after for a short time, it fails. Then I add some debug instructions in the emitted bytecodes to print something, and the output confused me for two weeks.

(GWT is short for GuardWithTestHandle).

1, Two classes are generated: DYNGWT70 and DYNGWT73, and both are loaded using Unsafe. For each class, there is only one instance is created.

2, The layout of DYNGWT70 is something like:

public class java.lang.invoke.DYNGuardWithTestHandle70 extends java.lang.invoke.BaseTemplate{
  public org.jruby.runtime.builtin.IRubyObject inlinedMethod(org.jruby.runtime.ThreadContext, org.jruby.runtime.builtin.IRubyObject, org.jruby.runtime.builtin.IRubyObject) throws java.lang.Throwable;
    flags: ACC_PUBLIC
    Code:
      stack=8, locals=22, args_size=4
         0: aload_0
         1: aload_0
         2: ldc           #29                 // String  This is Guard java/lang/invoke/DYNGuardWithTestHandle70
         4: invokestatic  #32                 // Method java/lang/invoke/BaseTemplate.tempDebug:(Ljava/lang/invoke/MethodHandle;Ljava/lang/String;)V
         7: astore        4
         9: aload         4
 .....
}
}

protected static void tempDebug(MethodHandle mh, String name){
    System.err.println("___________Debug: "+mh.getClass().getName()+", "+mh.toString()+ "  message="+name);
}

DYNGWT73 has similar structure.

But the output for the first tempDebug is:

___________Debug: java.lang.invoke.DYNGuardWithTestHandle73/0000000052DFAE40, MethodHandle(ThreadContext,IRubyObject,IRubyObject)IRubyObject uid:9a7bf505-8845-4594-9cf8-69f392eef869  message= This is Guard java/lang/invoke/DYNGuardWithTestHandle70
......
16/Aug/2016:22:13:42:834 -0300 [main] DEBUG java.lang.invoke.BaseTemplate - TypeInconsistException [_mh=MethodHandle(ThreadContext,IRubyObject,IRubyObject)IRubyObject uid:e064b157-f615-4f20-b386-947fc20c61ad, _exce=***** false (Lorg/jruby/runtime/ThreadContext;Lorg/jruby/runtime/builtin/IRubyObject;Lorg/jruby/runtime/builtin/IRubyObject;)Lorg/jruby/runtime/builtin/IRubyObject; (Lorg/jruby/runtime/ThreadContext;Lorg/jruby/runtime/builtin/IRubyObject;Lorg/jruby/runtime/builtin/IRubyObject;J)Lorg/jruby/runtime/builtin/IRubyObject;]
TypeInconsistException [_mh=MethodHandle(ThreadContext,IRubyObject,IRubyObject)IRubyObject uid:e064b157-f615-4f20-b386-947fc20c61ad, _exce=***** false (Lorg/jruby/runtime/ThreadContext;Lorg/jruby/runtime/builtin/IRubyObject;Lorg/jruby/runtime/builtin/IRubyObject;)Lorg/jruby/runtime/builtin/IRubyObject; (Lorg/jruby/runtime/ThreadContext;Lorg/jruby/runtime/builtin/IRubyObject;Lorg/jruby/runtime/builtin/IRubyObject;J)Lorg/jruby/runtime/builtin/IRubyObject;]
    at java.lang.invoke.BaseTemplate.debugCompareReceiverTypeMethodDesc(BaseTemplate.java:59)
    at java.lang.invoke.DYNGuardWithTestHandle70.0000000052B50680.inlinedMethod(Unknown Source)
    at java.lang.invoke.DYNGuardWithTestHandle70.0000000052B50680.invokeExact_thunkArchetype_L(Unknown Source)
    at java.lang.invoke.MutableCallSiteDynamicInvokerHandle.invokeExact_thunkArchetype_X(MutableCallSiteDynamicInvok

I can not understand mh.getClass().getName() is the java.lang.invoke.DYNGuardWithTestHandle73/0000000052DFAE40, it should be something DYNGuardWithTestHandle**70**/0000000052DFAExxx The purpose of posting stack here is to show that it is DYN70's method running and the exception is related to the confusing point,..

This error does not always occur, though the frequency is high. Anyone have experienced similar strange case? Thanks for your suggestion.

shijie xu
  • 1,975
  • 21
  • 52
  • Is it an error though? or just a difference between your expectation and reality? – user207421 Aug 17 '16 at 02:21
  • This is only a debug statement. The variable with index 0 should be **DYNGuardWithTestHandle70**. Because it is 73 here, then it results error in a later method invocation instruction, which only works with DYNGWT70. – shijie xu Aug 17 '16 at 02:26
  • I don’t think that it is a good idea to create custom subclasses of `MethodHandle`. That’s like asking for trouble. And it makes no sense, compared to the clean alternative of using a direct handle to generated code and letting the JVM do the inlining. And it causes you to loose the focus. The exception indicates a `long` parameter being present only in one signature, which is of course an inconsistent type. – Holger Aug 18 '16 at 13:56
  • Thanks @Holger, your analysis on exception is correct, and the exception can be explained by this topic that the class name for the variable 0 is some other classes (i.e., ``DYNGWT70`` and ``DYNGWT73`` has different method type, and the variable 0 in ``DYNGWT70`` refers to ``DYNGWT73`` ) – shijie xu Aug 18 '16 at 17:42
  • I can only repeat myself: I don’t think that it is a good idea to create custom subclasses of `MethodHandle`. By the way, a class printed in a stack trace doesn’t have to match the actual class of the object instance. It rather tells, where the implementation code came from… – Holger Aug 18 '16 at 18:02

1 Answers1

0

If you use unsafe nonstandard APIs, please at least read comments to it. Better not to rely on internal APIs at all. You are defining an anonymous class - don't expect it to have a valid name.

The comments to Unsafe.defineAnonymousClass say:

// When you load an anonymous class U, it works as if you changed its name just before loading,
// to a name that you will never use again.  Since the name is lost, no other class can directly
// link to any member of U.

So, /0000000052DFAE40 is intentionally appended by defineAnonymousClass. For example, you may load the same class bytes twice (what you can't do with a regular ClassLoader), and different suffixes will be generated to avoid name clash.

apangin
  • 92,924
  • 10
  • 193
  • 247
  • Thanks for your comments. I have a case that generates many classes, the majority of which might share the same class name (on the bytecode level). That's the reason I choose Unsafe API. The point in my question is not at the HEX string appended after ``/``, it is on the string before ``/``. The right class name should be something ``DYNGuardWithTestHandle70/...`` rather than ``DYNGuardWithTestHandle73/...`` – shijie xu Aug 17 '16 at 12:36
  • @shijiexu It is hard to say without seeing a full picture of your case including the source that generates bytecode and the source for loading generated classes. There must be a concurrency problem or whatsoever. Please try to build a complete verifyable example if possible. – apangin Aug 17 '16 at 12:56
  • @shijiexu The reason for using Unsafe API is still unclear. You can probably generate unique class names yourself or load the classes by different classloaders. – apangin Aug 17 '16 at 12:58
  • Thanks @apangin. I agree a small verifiable case would be helpful, which is much difficult for me currently. The using of ``Unsafe`` is that the anonymous class is much convenient to load a class's multiple versions. Anyway, i would try using normal classLoader, instead of Unsafe, for a test to see what will happen. So far, I do not have evidences it is class loader issues. – shijie xu Aug 17 '16 at 13:15