Creating TypeDescryption of Array in ByteBuddy - byte-buddy

I want to create classes like this in Byte-Buddy:
class A{
public B b;
}
class B{
public A[] a_array;
}
this is my code on how I imagine it could be done:
InstrumentedType arrayType = InstrumentedType.Default.of("[A",
TypeDescription.Generic.Builder.rawType(Object.class).build(),
Modifier.PUBLIC);
DynamicType.Unloaded B_made = new ByteBuddy()
.subclass(Object.class)
.name("B")
.defineField("a_array", arrayType, Modifier.PUBLIC)
.make();
DynamicType.Unloaded A_made = new ByteBuddy()
.subclass(Object.class)
.name("A")
.defineField("b", B_made.getTypeDescription(), Modifier.PUBLIC).make();
Class A = A_made.include(B_made).load(Test.class.getClassLoader()).getLoaded();
Class B = A.getField("b").getType();
System.out.println(B.getField("a_array"));
But this does not work even though Byte-Buddy will make the classes and load the them.
This code gives me this when evaluating B.getField("a_array")
Exception in thread "main" java.lang.NoClassDefFoundError: L[A;
at java.lang.Class.getDeclaredFields0(Native Method)
at java.lang.Class.privateGetDeclaredFields(Class.java:2583)
at java.lang.Class.getField0(Class.java:2975)
at java.lang.Class.getField(Class.java:1701)
at Test.main(Test.java:88)
Caused by: java.lang.ClassNotFoundException: [A
at net.bytebuddy.dynamic.loading.ByteArrayClassLoader.findClass(ByteArrayClassLoader.java:396)
at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
... 5 more
I was wondering how can I fix this?
is it even possible for me to create TypeDescription of [A from TypeDescription of A??

To construct an array type, use TypeDescription.Generic.Builder which allows for creating array representations. Do not use InstrumentedType.Default for creating arrays as they are intended to represent definable types.

Related

Byte Buddy: Creating class by implementing interface leads to NoClassFoundException

I am trying to create a class in a unit test that implements an Interface by using byte buddy
interface SomeInterface {}
class ByteBuddyTest {
#Test
fun byteBuddyTest(){
val instrumentation = ByteBuddyAgent.install()
val bb = ByteBuddy()
val loadedRestController = bb
.subclass(SomeInterface::class.java)
.make()
.load(Object::class.java.classLoader, ClassLoadingStrategy.Default.WRAPPER)
.loaded
}
}
Unfortunately I am getting a NoClassFoundException when trying to implement the Interface
org/example/SomeInterface
java.lang.NoClassDefFoundError: org/example/SomeInterface
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:756)
at net.bytebuddy.dynamic.loading.ByteArrayClassLoader.access$300(ByteArrayClassLoader.java:56)
at net.bytebuddy.dynamic.loading.ByteArrayClassLoader$ClassDefinitionAction.run(ByteArrayClassLoader.java:686)
at net.bytebuddy.dynamic.loading.ByteArrayClassLoader$ClassDefinitionAction.run(ByteArrayClassLoader.java:638)
at java.security.AccessController.doPrivileged(Native Method)
at net.bytebuddy.dynamic.loading.ByteArrayClassLoader.doPrivileged(ByteArrayClassLoader.java)
at net.bytebuddy.dynamic.loading.ByteArrayClassLoader.findClass(ByteArrayClassLoader.java:405)
at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
Am I missing something during agent initialization?
You are loading the class into a child loader of the boot loader by using
Object::class.java.classLoader
This class loader is of course unaware of SomeInterface. Instead, load the class into a child of:
SomeInterface::class.java.classLoader

ByteBuddy creating enums with constructors

Using ByteBuddy how can I create enum with constructors such as this one :
public enum EnumConstructorSample {
STATE1(10),
STATE2(15);
public int count;
EnumConstructorSample(int count){
this.count = count;
}
}
I tried this code and it gives me error.
Class enumClass = new ByteBuddy().makeEnumeration("STATE1", "STATE2")
.name("DynamicEnum")
.defineConstructor(Visibility.PACKAGE_PRIVATE)
.withParameters(int.class)
.intercept(FixedValue.value(1))
.make()
.load(EnumWithConstructor.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
System.out.println(enumClass.getDeclaredConstructors()[0]);
This is the Error and it is happening in enumClass.getDeclaredConstructors()
Exception in thread "main" java.lang.VerifyError: Constructor must call super() or this() before return
Exception Details:
Location:
DynamicEnum.<init>(I)V #2: return
Reason:
Error exists in the bytecode
Bytecode:
0x0000000: 0457 b1
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
at java.lang.Class.getDeclaredConstructors(Class.java:2020)
at EnumWithConstructor.main(EnumWithConstructor.java:19)
For constructors, it is required to invoke the super method within the constructor. For enumerations, you'd need to invoke the Enum(String, int) constructor. You can implement this using MethodCall.invoke(...).onSuper().
If you wanted to achieve this, I'd recommend you to subclass Enum manually since you'd otherwise define multiple constructors for the enum you are creating where Byte Buddy would invoke its own enum constructor and the fields would all have its default value.
Rather, implement the method and return the value based on its name. You can for example use a MethodDelegation and then use a #This Enum<?> val injection where you switch over the name to return the correct value as if it was stored in a field.

How can I make Kryo Serializer

I tried to solve the following problem by providing Kryo Serializer but it still doesn't work. It could not recognize the serializer of ModelCom. Also, any messages by print function don't show up.
I used Apache Flink 1.9.0 and Apache Jena 3.10.0
My code in Kotlin:
val serializer = object : Serializer<Model>(){
override fun write(kryo: Kryo, output: Output?, obj : Model?) {
print("write")
kryo.writeClassAndObject(output, obj)
}
override fun read(kryo: Kryo, input: Input?, type: Class<Model>?): Model {
print("read")
val m = kryo.readObject(input, Model::class.java)
return m
}
}
ExecutionContext.see.config.registerTypeWithKryoSerializer(ModelCom::class.java, serializer::class.java)
Error
Exception in thread "main" org.apache.flink.streaming.runtime.tasks.StreamTaskException: Cannot serialize operator object class org.apache.flink.streaming.api.operators.SimpleUdfStreamOperatorFactory.
at org.apache.flink.streaming.api.graph.StreamConfig.setStreamOperatorFactory(StreamConfig.java:222)
at org.apache.flink.streaming.api.graph.StreamingJobGraphGenerator.setVertexConfig(StreamingJobGraphGenerator.java:460)
at org.apache.flink.streaming.api.graph.StreamingJobGraphGenerator.createChain(StreamingJobGraphGenerator.java:272)
at org.apache.flink.streaming.api.graph.StreamingJobGraphGenerator.createChain(StreamingJobGraphGenerator.java:243)
at org.apache.flink.streaming.api.graph.StreamingJobGraphGenerator.createChain(StreamingJobGraphGenerator.java:243)
at org.apache.flink.streaming.api.graph.StreamingJobGraphGenerator.setChaining(StreamingJobGraphGenerator.java:207)
at org.apache.flink.streaming.api.graph.StreamingJobGraphGenerator.createJobGraph(StreamingJobGraphGenerator.java:159)
at org.apache.flink.streaming.api.graph.StreamingJobGraphGenerator.createJobGraph(StreamingJobGraphGenerator.java:94)
at org.apache.flink.streaming.api.graph.StreamGraph.getJobGraph(StreamGraph.java:737)
at org.apache.flink.optimizer.plan.StreamingPlan.getJobGraph(StreamingPlan.java:40)
at org.apache.flink.streaming.api.environment.LocalStreamEnvironment.execute(LocalStreamEnvironment.java:86)
at org.apache.flink.streaming.api.environment.StreamExecutionEnvironment.execute(StreamExecutionEnvironment.java:1507)
at org.apache.flink.streaming.api.environment.StreamExecutionEnvironment.execute(StreamExecutionEnvironment.java:1489)
at core.EgressEngine.start(EgressEngine.kt:187)
at core.EgressEngineKt.main(EgressEngine.kt:45)
Caused by: java.io.NotSerializableException: org.apache.jena.rdf.model.impl.ModelCom
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1185)
at java.base/java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1553)
at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1510)
at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1433)
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1179)
at java.base/java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1553)
at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1510)
at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1433)
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1179)
at java.base/java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1553)
at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1510)
at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1433)
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1179)
at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:349)
at org.apache.flink.util.InstantiationUtil.serializeObject(InstantiationUtil.java:586)
at org.apache.flink.util.InstantiationUtil.writeObjectToConfig(InstantiationUtil.java:515)
at org.apache.flink.streaming.api.graph.StreamConfig.setStreamOperatorFactory(StreamConfig.java:219)
... 14 more
Jena models are not serializable, so this approach isn't going to work. What you could do instead would be to send around just enough serialized data so that each instance that needs a model can instantiate one.
See this thread from the jena-users list about how to resolve this for Spark; the underlying issues are the same for any JVM-based framework that distributes computation.

Kotlin implement a method from an interface that's already present in super class

Let's say I have this interface:
interface Things {
fun size(): Int
}
And I want to subclass a List and implement this interface.
class Cars : ArrayList<String>, Things {}
I get a compilation error:
Inherited platform declarations clash: The following declarations have
the same JVM signature (size()I): fun (): Int defined in
Things fun size(): Int defined in Things
I can get around the compilation error by changing the size contract to a var implicit getter:
interface Things {
var size: Int
}
But then I get a long and complicated error that looks like a runtime error (IllegalStateException) but seems to happen when Kotlin is compiling :shrug:
I suspect I know why this is happening - ArrayList already has int size() and Kotlin now tries to add another of the same signature, but even if I'm right (10% chance?) it doesn't help much.
I'll also mention that it allows me to have get as part of the Things interface, which is satisfied by the list. I'm guessing the operator operator let's this be just different enough to slide?
So anyway, no IDE errors, but when I build I get this:
java.lang.IllegalStateException: Backend Internal error: Exception during code generation
Cause: Concrete fake override public open fun <set-size>(<set-?>: kotlin.Int): kotlin.Unit defined in org.blah.Stuff[PropertySetterDescriptorImpl#673f2280] should have exactly one concrete super-declaration: []
File being compiled at position: file:///blah/Stuff.kt
The root cause was thrown at: bridges.kt:122
at org.jetbrains.kotlin.codegen.CompilationErrorHandler.lambda$static$0(CompilationErrorHandler.java:24)
at org.jetbrains.kotlin.codegen.PackageCodegenImpl.generate(PackageCodegenImpl.java:76)
at org.jetbrains.kotlin.codegen.DefaultCodegenFactory.generatePackage(CodegenFactory.kt:96)
at org.jetbrains.kotlin.codegen.DefaultCodegenFactory.generateModule(CodegenFactory.kt:67)
at org.jetbrains.kotlin.codegen.KotlinCodegenFacade.doGenerateFiles(KotlinCodegenFacade.java:47)
at org.jetbrains.kotlin.codegen.KotlinCodegenFacade.compileCorrectFiles(KotlinCodegenFacade.java:39)
at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.generate(KotlinToJVMBytecodeCompiler.kt:476)
at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:164)
at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:166)
at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:56)
at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:84)
at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:42)
at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:104)
at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:349)
at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:105)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileIncrementally(IncrementalCompilerRunner.kt:237)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.access$compileIncrementally(IncrementalCompilerRunner.kt:37)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner$compile$2.invoke(IncrementalCompilerRunner.kt:79)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:91)
at org.jetbrains.kotlin.daemon.CompileServiceImpl.execIncrementalCompiler(CompileServiceImpl.kt:579)
at org.jetbrains.kotlin.daemon.CompileServiceImpl.access$execIncrementalCompiler(CompileServiceImpl.kt:102)
at org.jetbrains.kotlin.daemon.CompileServiceImpl$compile$$inlined$ifAlive$lambda$2.invoke(CompileServiceImpl.kt:455)
at org.jetbrains.kotlin.daemon.CompileServiceImpl$compile$$inlined$ifAlive$lambda$2.invoke(CompileServiceImpl.kt:102)
at org.jetbrains.kotlin.daemon.CompileServiceImpl$doCompile$$inlined$ifAlive$lambda$2.invoke(CompileServiceImpl.kt:1005)
at org.jetbrains.kotlin.daemon.CompileServiceImpl$doCompile$$inlined$ifAlive$lambda$2.invoke(CompileServiceImpl.kt:102)
at org.jetbrains.kotlin.daemon.common.DummyProfiler.withMeasure(PerfUtils.kt:138)
at org.jetbrains.kotlin.daemon.CompileServiceImpl.checkedCompile(CompileServiceImpl.kt:1047)
at org.jetbrains.kotlin.daemon.CompileServiceImpl.doCompile(CompileServiceImpl.kt:1004)
at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:454)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:346)
at sun.rmi.transport.Transport$1.run(Transport.java:200)
at sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:568)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:826)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:683)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:682)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.IllegalStateException: Concrete fake override public open fun <set-size>(<set-?>: kotlin.Int): kotlin.Unit defined in org.blah.Stuff[PropertySetterDescriptorImpl#673f2280] should have exactly one concrete super-declaration: []
at org.jetbrains.kotlin.backend.common.bridges.BridgesKt.findConcreteSuperDeclaration(bridges.kt:122)
at org.jetbrains.kotlin.backend.common.bridges.BridgesKt.generateBridges(bridges.kt:59)
at org.jetbrains.kotlin.codegen.JvmBridgesImplKt.generateBridgesForFunctionDescriptorForJvm(JvmBridgesImpl.kt:92)
at org.jetbrains.kotlin.codegen.FunctionCodegen.generateBridges(FunctionCodegen.java:1041)
at org.jetbrains.kotlin.codegen.ClassBodyCodegen.generateBridges(ClassBodyCodegen.java:138)
at org.jetbrains.kotlin.codegen.ClassBodyCodegen.generateBody(ClassBodyCodegen.java:116)
at org.jetbrains.kotlin.codegen.MemberCodegen.generate(MemberCodegen.java:129)
at org.jetbrains.kotlin.codegen.MemberCodegen.genClassOrObject(MemberCodegen.java:302)
at org.jetbrains.kotlin.codegen.MemberCodegen.genClassOrObject(MemberCodegen.java:286)
at org.jetbrains.kotlin.codegen.PackageCodegenImpl.generateClassesAndObjectsInFile(PackageCodegenImpl.java:118)
at org.jetbrains.kotlin.codegen.PackageCodegenImpl.generateFile(PackageCodegenImpl.java:137)
at org.jetbrains.kotlin.codegen.PackageCodegenImpl.generate(PackageCodegenImpl.java:68)
... 44 more
> Task :app:buildInfoGeneratorDebug
Anything I'm missing. I've spent most of my life in Java and am late to the Kotlin party. Any workarounds, alternatives... or is this a known thing?
TYIA
The problem is because kotlin have extension val property for List: size.
The first error says that you have two different kotlin "things" (method and property) which in jvm are the same. And the second error is because you have val implementation in ArrayList and var field in interface so your class Cars needs to implement both setter and getter but implements only getter (from List val size).
The solution is simple: just change your interface property to val size: Int

How to derive from a base class when the base initializer is available only through a helper function?

This is a general Object Oriented Programming question:
Suppose I am given a base class B:
class B {
// member functions
}
and suppose I am told to create an instance of B through the following factory method:
B createB(/* arguments */) {
b = ...
return b;
}
Now, the problem is that I need to derive from B but how am I going to initialize it as createB() does?:
class D : B {
D() {
/* need to use createB() to
initialize the base because
no equivalent constructor
exists. */
}
}
Using factory methods is in many aspects much better than just directly calling constructors. Once you've chosen using factory methods, you should stick to this approach for all the derived class - otherwise you violate the basic reason for why the factory methods have been introduced: separate the object creation from object usage.
Hence, instead of solving the issue of accessing the factory method for class B from the constructor of class D, think about creating a factory method also for the class D. Or even better: create a common factory method B createB(arguments) which decides itself what type it creates and returns.