Byte Buddy causes IncompatibleClassChangeError - java

I use Byte Buddy (v0.5.2) to dynamically create a "subclass" of an interface (actually, I want to create a class that implements that interface). All methods invoked on an instance of this class should be redirected to another (interceptor) class.
I used the following code (with "TestInterface" being an interface that declares exactly one method "sayHello"):
final Interceptor interceptor = new Interceptor();
Class<?> clazz = new ByteBuddy()
.subclass(TestInterface.class)
.method(any()).intercept(MethodDelegation.to(interceptor))
.make()
.load(TestInterface.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
.getLoaded();
TestInterface instance = (TestInterface) clazz.newInstance();
instance.sayHello();
The interceptor class looks like this:
public class Interceptor {
public Object intercept(#Origin MethodHandle method, #AllArguments Object[] args) throws Throwable {
...
}
}
However, when I try to call the "sayHello" method (last line of my code example), I get an "IncompatibleClassChangeError". The stack trace is as follows:
Exception in thread "main" java.lang.IllegalAccessError: no such method: byteuddytest.TestInterface.sayHello()void/invokeVirtual
at java.lang.invoke.MethodHandleNatives.linkMethodHandleConstant(MethodHandleNatives.java:448)
at bytebuddytest.TestInterface$ByteBuddy$0E9xusGs.sayHello(Unknown Source)
at bytebuddytest.Main.main(Main.java:32)
Caused by: java.lang.IncompatibleClassChangeError: Found interface bytebuddytest.TestInterface, but class was expected
at java.lang.invoke.MethodHandleNatives.resolve(Native Method)
at java.lang.invoke.MemberName$Factory.resolve(MemberName.java:965)
at java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:990)
at java.lang.invoke.MethodHandles$Lookup.resolveOrFail(MethodHandles.java:1387)
at java.lang.invoke.MethodHandles$Lookup.linkMethodHandleConstant(MethodHandles.java:1732)
at java.lang.invoke.MethodHandleNatives.linkMethodHandleConstant(MethodHandleNatives.java:442)
... 2 more
The problem seems to be related to the use of the "MethodHandle" parameter in my interceptor method. When I change the type to "Method", everything works fine. But according to the docs, "MethodHandle" should be preferred to "Method" because of performance reasons.
Is the error caused by a bug in Byte Buddy, or should I actually use a "Method" parameter in this case?

Use a Method parameter and enable caching. That should solve most of your performance issues, if you have any in the first place.
See javadoc for #Origin:
public abstract boolean cacheMethod
If this value is set to true and the annotated parameter is a Method type, the value that is assigned to this parameter is cached in a static field. Otherwise, the instance is looked up from its defining Class on every invocation of the intercepted method.
Method look-ups are normally cached by its defining Class what makes a repeated look-up of a method little expensive. However, because Method instances are mutable by their AccessibleObject contact, any looked-up instance needs to be copied by its defining Class before exposing it. This can cause performance deficits when a method is for example called repeatedly in a loop. By enabling the method cache, this performance penalty can be avoided by caching a single Method instance for any intercepted method as a static field in the instrumented type.

See the answer of Jeor which is totally correct (you should mark it as accepted). Just two remarks that do not fit into a comment:
You should of course only use a MethodHandle instead of a Method if the former allows you what you do. Invoking MethodHandles implies some JVM magic. Handles are resolved with a polymorphic signature by a JVM, i.e. their arguments must not be boxed as the JVM will simply replace the call site with a method call. In your case, this does therefore not work. The advantage of a method handle is however that it can be stored in the constant pool of a class. It is a native concept that can be accessed by a byte code instruction. Compared to that, a Method reference needs to be produced explicitly.
You should therefore rather cache the Method instance (which is mutable!). Also, note that you are currently also intercepting the methods of Object. You can clean up your code a bit by:
Class<? extends TestInterface> clazz = new ByteBuddy()
.subclass(TestInterface.class)
.method(isDeclaredBy(TestInterface.class))
.intercept(MethodDelegation.to(interceptor))
.make()
.load(TestInterface.class.getClassLoader(),
ClassLoadingStrategy.Default.INJECTION)
.getLoaded();
TestInterface instance = clazz.newInstance();

Related

Byte Buddy instantiate class without parameters for constructor

I am trying to instantiate a class that doesn't have an empty parameter constructor (and it's direct parent also doesn't have an empty parameter constructor)
Class<?> newClass = new ByteBuddy();
.subclass(BufferedImage.class)
...
.make()
.load(BufferedImage.class.getClassLoader())
.getLoaded();
BufferedImage bufferedImage = dynamicTypeBufferedImage.getConstructor().newInstance();
I am wondering if this is possible using byte buddy. Currently I am getting an error saying that the getConstructor() of the proxy class is not found which makes sense because the empty parameter constructor doesn't exist. Is there a way to define the empty parameter constructor such that this instantiation works?
I have tried:
...
.constructor(any()).intercept(to(new Object() {
public void construct() throws Exception {
System.out.println("CALLING XTOR");
}
}).andThen(SuperMethodCall.INSTANCE)) // This makes the difference!
...
which came from here and gave me the error of Image class doesn't have super() (which is the parent class of BufferedImage).
I also tried:
...
.defineConstructor(Visibility.PUBLIC)
.intercept(MethodCall
.invoke(superClass.getDeclaredConstructor())
.onSuper())
...
which came from here
Lastly, I also tried the .subclass(type, ConstructorStrategy.Default.IMITATE_SUPER_CLASS); way to imitate super class but this doesn't seem to add an empty parameter constructor.
This functionality that I want, mimics the way cglib instantiates its objects using the enhancer. I know from what I have been reading that byte buddy is supposed to let the user decide how to instantiate. I was wondering if there was an easy way to just set the default instantiation to the empty parameter constructor since I don't care about setting fields in the class but rather just want to control the method space?
It is not legal to define a Java constructor that does not invoke its super constructor in some way (either directly or via another constructor), since it cannot be expressed in byte code, it cannot be done in Byte Buddy.
What you can do is:
Not define a constructor alltogether using ConstructorStrategy.Default.NO_CONSTRUCTORS.
Use a library like Objenesis to instantiate a class.
Note however that Objenesis is using unsafe API that might not be supported in a future Java version.

Is it possible to call constructor on existing instance?

It is known that using sun.misc.Unsafe#allocateInstance one can create an object without calling any class constructors.
Is it possible to do the opposite: given an existing instance, invoke a constructor on it?
Clarification: this is not the question about something I'd do in production code. I'm curious about JVM internals and crazy things that can still be done. Answers specific to some JVM version are welcome.
JVMS §2.9 forbids invocation of constructor on already initialized objects:
Instance initialization methods may be invoked only within the Java
Virtual Machine by the invokespecial instruction, and
they may be invoked only on uninitialized class instances.
However, it is still technically possible to invoke constructor on initialized object with JNI. CallVoidMethod function does not make difference between <init> and ordinary Java methods. Moreover, JNI specification hints that CallVoidMethod may be used to call a constructor, though it does not say whether an instance has to be initialized or not:
When these functions are used to call private methods and constructors, the method ID must be derived from the real class of obj, not from one of its superclasses.
I've verified that the following code works both in JDK 8 and JDK 9. JNI allows you to do unsafe things, but you should not rely on this in production applications.
ConstructorInvoker.java
public class ConstructorInvoker {
static {
System.loadLibrary("constructorInvoker");
}
public static native void invoke(Object instance);
}
constructorInvoker.c
#include <jni.h>
JNIEXPORT void JNICALL
Java_ConstructorInvoker_invoke(JNIEnv* env, jclass self, jobject instance) {
jclass cls = (*env)->GetObjectClass(env, instance);
jmethodID constructor = (*env)->GetMethodID(env, cls, "<init>", "()V");
(*env)->CallVoidMethod(env, instance, constructor);
}
TestObject.java
public class TestObject {
int x;
public TestObject() {
System.out.println("Constructor called");
x++;
}
public static void main(String[] args) {
TestObject obj = new TestObject();
System.out.println("x = " + obj.x); // x = 1
ConstructorInvoker.invoke(obj);
System.out.println("x = " + obj.x); // x = 2
}
}
It seems that with some (very dubious) tricks this is possible, even without going through a custom native library, by (ab)using method handles.
This method essentially tricks the JVM into thinking it is currently invoking a regular method instead of a constructor.
I just have to add a mandatory "this is probably not a good idea", but this is the only way I found for doing this. I also can't attest to how this behaves on different JVMs.
Prerequisites
To do this, an instance of sun.misc.Unsafe is needed. I will not go into detail about how to obtain this here since you already seem to have one, but this guide explains the process.
Step 1: Obtaining a trusted MethodHandles.Lookup
Next, a java.lang.invoke.MethodHandles$Lookup is needed to get the actual method handle for the constructor.
This class has a permission system which works through the allowedModes property in Lookup, which is set to a bunch of Flags. There is a special TRUSTED flag that circumvents all permission checks.
Unfortunately, the allowedModes field is filtered from reflection, so we cannot simply bypass the permissions by setting that value through reflection.
Even though reflecion filters can be circumvented aswell, there is a simpler way: Lookup contains a static field IMPL_LOOKUP, which holds a Lookup with those TRUSTED permissions. We can get this instance by using reflection and Unsafe:
var field = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
var fieldOffset = unsafe.staticFieldOffset(field);
var lookup = (MethodHandles.Lookup) unsafe.getObject(MethodHandles.Lookup.class, fieldOffset);
We use Unsafe here instead of setAccessible and get, because going through reflection will cause issues with the module system in the newer java versions.
Step 2: Finding the constructor
Now we can get a MethodHandle for the constructor we want to invoke. We do this by using the Lookup we just obtained, just like a Lookup would be used normally.
var type = MethodType.methodType(Void.TYPE, <your constructor argument types>);
var constructor = lookup.findConstructor(<your class>, type);
Step 3: Getting the MemberName
While the signature of findConstructor only specifies that it returns a MethodHandle, it actuall returns a java.lang.invoke.DirectMethodHandle$Constructor. This type declares a initMethod field, which contains the java.lang.invoke.MemberName referencing our constructor. The MemberName type is not accessible from the outside, so all interaction with it happens through Unsafe.
We can obtain this MemberName in the same way we also obtained the Lookup:
var constructorClass = Class.forName("java.lang.invoke.DirectMethodHandle$Constructor");
val initMethodField = constructorClass.getDeclaredField("initMethod");
val initMethodFieldOffset = unsafe.objectFieldOffset(initMethodField);
var initMemberName = unsafe.getObject(constructor, initMethodFieldOffset)
Step 4: Tricking Java
The next step is the important part. While there are no physical barriers from the JVM that prevent you from invoking a constructor like any other method, MethodHandle has some checks in place to ensure that you are not doing something fishy.
Most of the checks are circumvented by using the TRUSTED Lookup, and there remains one final check:
The MemberName instance contains a bunch of flags that, among other things, tell the system what kind of member the MemberName is referring to. These flags are checked.
To circumvent this, we can simply change the flags using Unsafe:
var memberNameClass = Class.forName("java.lang.invoke.MemberName");
var flagsField = memberNameClass.getDeclaredField("flags");
var flagsFieldOffset = unsafe.objectFieldOffset(flagsField);
var flags = unsafe.getInt(initMemberName, flagsFieldOffset);
flags &= ~0x00020000; // remove "is constructor"
flags |= 0x00010000; // add "is (non-constructor) method"
unsafe.putInt(initMemberName, flagsFieldOffset, flags);
The values for the flags come from java.lang.invoke.MethodHandleNatives.Constants#MN_IS_METHOD and java.lang.invoke.MethodHandleNatives.Constants#MN_IS_CONSTRUCTOR.
Step 5: Obtaining a REF_invokeVirtual method handle
Now that we have a totally legit method that is not at all a constructor, we just need to obtain a regular method handle for invoking it. Luckly, MethodHandles.Lookup.class has a private method for turning a MemberName into a (Direct)MethodHandle for all kinds of invocations: getDirectMethod.
Ironically, we actually call this method using our all-powerful lookup.
First, we obtain the MethodHandle for getDirectMethod:
var getDirectMethodMethodHandle = lookup.findVirtual(
MethodHandles.Lookup.class,
"getDirectMethod",
MethodType.methodType(
MethodHandle.class,
byte.class,
Class.class,
memberNameClass,
MethodHandles.Lookup.class
)
);
we can now use this with our lookup, to obtain a MethodHandle for our MemberName:
var handle = (MethodHandle) getDirectMethod.invoke(lookup, (byte) 5, Test.class, member, lookup);
The (byte) 5 argument stands for "invoke virtual", and comes from java.lang.invoke.MethodHandleNatives.Constants#REF_invokeVirtual.
Step 6: Profit?
We can now use this handle like a regular MethodHandle, to invoke the constructor on any existing instance of that class:
handle.invoke(<instance>, <constructor arguments...>);
With this handle, the constructor can also be called multiple times, and the instance doesn't actually have to come from Unsafe#allocateInstance - an instance that was created just by using new works aswell.
A constructor is not an instance method, so no you can't invoke a constructor on an instance.
If you look at the reflection library, you'll see that the return type of Class.getConstructor() is Constructor, which doesn't have any methods that can accept a instance - its only relevant method is newInstance(), which doesn't accept a target instance; it creates one.
On the other hand, the return type of Class.getMethod() is Method, whose first parameter is the instance.
A Constructor is not a Method.
In the JVM spec for invokespecial:
An invokespecial instruction is type safe iff all of the following are true:
... (Stuff about non-init methods)
MethodName is <init>.
Descriptor specifies a void return type.
One can validly pop types matching the argument types given in Descriptor and an uninitialized type, UninitializedArg, off the incoming operand stack, yielding OperandStack.
...
If you've already initialized the instance, it's not an uninitialized type, so this will fail.
Note that other invoke* instructions (invokevirtual, invokeinterface, invokestatic, invokedynamic) explicitly preclude invocation of <init> methods, so invokespecial is the only way to invoke them.
From JLS Sec 8.8
Constructors are invoked by class instance creation expressions (§15.9), by the conversions and concatenations caused by the string concatenation operator +(§15.18.1), and by explicit constructor invocations from other constructors (§8.8.7). 
...
Constructors are never invoked by method invocation expressions (§15.12).
So no, it's not possible.
If there is some common action you want to take in the constructor and elsewhere, put it into a method, and invoke that from the constructor.

Why we are allowed to create an object of a class inside the same class?

I am just trying to figure out why it is possible to do such thing but it is not possible in some other programming languages like C++.
Here is an example of what I am asking:
public class A{
private A objectOfTheSameClass;
public A()
{
objectOfTheSameClass=new A();
}
}
Because it's not explicitly prohibited.
JLS section 8.8.7 that defines that body of a constructor does not mandate a compile-time error for such a case. The only case when a compile-time error should happen is when a constructor invokes itself with this:
It is a compile-time error for a constructor to directly or indirectly invoke itself through a series of one or more explicit constructor invocations involving this.
More precisely:
Except for the possibility of explicit constructor invocations, and the prohibition on explicitly returning a value (§14.17), the body of a constructor is like the body of a method (§8.4.7).
Since a method is allowed to invoke itself (recursion), a constructor is also allowed to do the same.
This will not compile because it calls this:
public class A{
private A objectOfTheSameClass;
public A() {
objectOfTheSameClass= this();
}
}
Your code, although it compiles, will throw a StackOverflowError because the constructor of A creates itself a new instance of A. The compiler can't detect every infinite loop in your code...
It can be useful sometimes, for example in the "singleton" pattern is used to have exactly one instance of a class by creating an object of the same class as a class field, making it accessible via a public method and making the constructor private.
The instances are not created until the runtime, so the class is fully defined. It's up to you to use this in a proper way.

Calling a function that does not exist via reflection/javassist

i need to call a function - (getters and setters of a property), but my problem is, the name of the property is generated at runtime (name of some variable + number).
Is it possible to manipulate the bytecode via javassist or gclib so that the function calls are directed to some proxy object/function, and there the real method name and the number extracted from the called function name, so that i can call the function afterwards (with the number as parameter)?
I tried the following, but it didnt work:
MethodHandler handler = new MethodHandler() {
#Override
public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) {
String realMethodName=thisMethod.substring(0,5);
Integer param=Integer.parseInt(thisMethod.substring(5));
Method m = self.getClass().getMethod(realMethodName);
m.invoke(self,param);
return null;
}
};
I think this might be one of the few scenarios where using Java reflection Proxy objects might come in handy.
You could define some interface, but delegate the method to the (dynamic) invocation handler which would then call those "getter/setter" methods.
Side note: when implementing such an invocation handler you have to understand that ANY method call on the corresponding object will trigger its "invoke" method; event when you call toString or equals or whatever else is inherited from Object.
EDIT: and one more (different) thought: are you sure you need to create dynamic method names at all? If you have some numeric (or string based) key - what not using a Map for example?
Like
Map<WhateverKeyType,YourPropertyClass>
that would be much more "normal java" way of handling your problem (instead of thinking about reflection or byte code manipulation).
If you wanted to implement something similar but could think of using another library than javassist, consider using Byte Buddy (which I wrote, for disclosure).
Considering, you have an interface
interface Foo { Object getProperty() }
that you wanted to implement to access a property of the bean
class Bar { Object getAbc123() { ... } }
Then using Byte Buddy, you could implement a class
Foo accessor = new ByteBuddy()
.subclass(Foo.class)
.method(named("getProperty"))
.intercept(MethodCall.invoke(Bar.class.getDeclaredMethod("getAbc123"))
.on(new Bar()))
.make()
.load(Foo.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded()
.newInstance();
of the interface that redirects the invocation of getProperty to the method of your choosing. With a little bit of customization, you can surely create a more generic solution.

how to access instance that 'owns' a method in java?

in java, is it possible to access the instance to which a method belongs, given only the method?
for example:
public class ClassA {
private ClassB instanceB = new ClassB();
// ...
private void sendMethod () {
instanceB.receiveMethod(foo);
}
public void foo () {}
}
public class ClassB {
public void receiveMethod (Method method) {
Object o = foo.getInstanceOwner(); // just made that part up...
}
}
my feeling is that methods belong to classes, not instances of a class, so the answer is no, but maybe there's some sneaky reflection technique i don't know about. i could always pass 'this' along with method foo, but that seems like extra baggage.
Taken from
A Method provides information about, and access to, a single method on a class or interface. The reflected method may be a class method or an instance method (including an abstract method).
A Method permits widening conversions to occur when matching the actual parameters to invoke with the underlying method's formal parameters, but it throws an IllegalArgumentException if a narrowing conversion would occur.
You can call Method#invoke but you will need the instance of the object you want to call the method on, from the method doc:
Invokes the underlying method
represented by this Method object, on
the specified object with the
specified parameters. Individual
parameters are automatically unwrapped
to match primitive formal parameters,
and both primitive and reference
parameters are subject to method
invocation conversions as necessary.
If the underlying method is static,
then the specified obj argument is
ignored. It may be null.
If the number of formal parameters
required by the underlying method is
0, the supplied args array may be of
length 0 or null.
If the underlying method is an
instance method, it is invoked using
dynamic method lookup as documented in
The Java Language Specification,
Second Edition, section 15.12.4.4; in
particular, overriding based on the
runtime type of the target object will
occur.
If the underlying method is static,
the class that declared the method is
initialized if it has not already been
initialized.
If the method completes normally, the
value it returns is returned to the
caller of invoke; if the value has a
primitive type, it is first
appropriately wrapped in an object.
However, if the value has the type of
an array of a primitive type, the
elements of the array are not wrapped
in objects; in other words, an array
of primitive type is returned. If the
underlying method return type is void,
the invocation returns null.
So the TL:DR is unless you have the actual object you want you call the method on, it is not possible.
public class ClassA {
private ClassB instanceB = new ClassB();
// ...
private void sendMethod () {
Method m = ClassA.class.getMethod("foo", null);
instanceB.receiveMethod(m);
}
public void foo () {}
}
public class ClassB {
public void receiveMethod (Method method) {
Class c = method.getDeclaringClass();
}
}
gives you the owning Class. An instance doesn't own methods.
You can do this, but the proper way in your example would be the use of an interface, because that seems to be what you want: You want to pass in an object that ClassB knows how to operate on.
interface Callback {
void foo();
}
public class ClassA implements Callback {...}
public class ClassB {
public void receiveMethod(Callback cb) {...}
}
This is like asking:
"Given an apple from an Apple orchard, which tree owns this apple?"
The answer to which is:
"No idea, since all apple trees produce apples, it could belong to any tree".
... in other words - you must supply an instance from which the method will be called
EDIT
From one of your comments, I gather you are looking for an alternative of the Observer pattern. You say you don't like the messiness of the Observer pattern and that it is not "generic" enough for you.
I would argue that it is probably one of the least messiest patterns in existence, AND interfaces are by definition as generic as things get!
So, perhaps its an implementation problem you're having. Luckily, I have already posted on SO an Observer implementation in JAVA, to demonstrate how powerful and elegant it is.
Polymorphism and Interfaces in Java (can polymorphism be used to implement interfaces...why?)
In fact: reflection is messier than using an interface, since you can't guarantee at compile time that the type of Object you are invoking an instance of a Method on, even supports that method! (without some error checking code). Versus with interfaces, its not possible to even have that problem.

Categories