Reputation: 1295
I am using Byte Buddy 1.9.0 as a code generator for an Xtext-based programming language, and I am struggling to produce bytecode for a method that instantiates an "anonymous" class. For all intents and purposes, I'm trying to have Byte Buddy create a method that does the equivalent of this:
package fxxx.test;
class DummyUser
{
DummyOperator dummy()
{
return new DummyUser$1();
}
}
My compiler has already created the nested "anonymous" class DummyUser$1
, but, for the life of me, I cannot come up with the correct Byte Buddy instruction sequence to create a new object of that class and then return that object.
After some searching I came across MethodCall.construct(MethodDescription)
, but my current code only produces the following exception:
java.lang.IllegalStateException: Cannot return void from public fxxx.test.DummyOperator fxxx.test.DummyUser_.dummy()
at net.bytebuddy.implementation.MethodCall$TerminationHandler$1.resolve(MethodCall.java:2189)
at net.bytebuddy.implementation.MethodCall.toStackManipulation(MethodCall.java:2405)
at net.bytebuddy.implementation.MethodCall$Appender.apply(MethodCall.java:2434)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyCode(TypeWriter.java:698)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyBody(TypeWriter.java:683)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod.apply(TypeWriter.java:590)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForCreation.create(TypeWriter.java:5114)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1915)
at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:197)
at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:174)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:3376)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Delegator.make(DynamicType.java:3565)
...
This error seems to tell me, somewhat understandably, that since the constructor for the class has an <init>()V
signature and technically returns void
this is equivalent to returning a void
value from a method that should really return some subclass of DummyOperator
. I tried to combine the MethodCall.construct
with a StackManipulation
that duplicates the top of the stack, but as the TypeCreation
via the new
bytecode is already baked into the MethodCall.construct
I don't seem to have access to the newly created raw object instance.
The project is not open-sourced yet, but I can provide this snippet (using Xtend syntax) to illustrate what my code is doing:
val Builder<?> anonymousClass = ... // the anonymous class that has already been created
val TypeDescription declaringType = describe(anonymousClass) // basically .make.typeDescription
val MethodDescription constructor = method(declaringType, new TypeDescription.ForLoadedType(void), "<init>", #[]) // new Latent declaringType/returnType
val MethodCall construct = MethodCall.construct(constructor)
// ^^^ this value gets return as the new method's Implementation
What am I missing so that the method will properly return the newly created instance?
Upvotes: 1
Views: 804
Reputation: 1295
So, it looks like I wasn't missing any steps after all, or necessarily doing anything wrong about the object instantiation itself. When I ran this in the debugger, I noticed that the invalid stack manipulation was caused by a type mismatch, which indicated that the return type of method was actually not compatible with the type of the anonymous class (mistake on my part; the language's type system is a little more complex than, let's say, plain Java, and the two types were incompatible in a subtle way). The lesson learned, I guess, is to double-check the type compatibility when encountering exceptions like the above.
Upvotes: 2