quarkusio / gizmo Goto Github PK
View Code? Open in Web Editor NEWLicense: Apache License 2.0
License: Apache License 2.0
It should use ireturn
instead.
This code exhibits the exception:
public class GizmoTest {
public static class MyEntity {
boolean isPersistent;
long version;
}
public static void main(String[] args) {
try(ClassCreator modelClass = ClassCreator.builder().className("test")
.classOutput((foo, bar) -> {
System.err.println("Printing "+foo+": "+bar);
})
.build()){
FieldDescriptor fieldDescriptor = FieldDescriptor.of(MyEntity.class, "version", long.class);
try(MethodCreator creator = modelClass.getMethodCreator("foo", void.class, MyEntity.class)){
ResultHandle entityParam = creator.getMethodParam(0);
creator.writeInstanceField(fieldDescriptor, entityParam, creator.load(0));
creator.returnValue(creator.loadNull());
}
}
}
}
When running it, I get:
Exception in thread "main" java.lang.NegativeArraySizeException
at org.objectweb.asm.Frame.merge(Frame.java:1222)
at org.objectweb.asm.MethodWriter.computeAllFrames(MethodWriter.java:1607)
at org.objectweb.asm.MethodWriter.visitMaxs(MethodWriter.java:1543)
at org.objectweb.asm.MethodVisitor.visitMaxs(MethodVisitor.java:762)
at io.quarkus.gizmo.MethodCreatorImpl.write(MethodCreatorImpl.java:107)
at io.quarkus.gizmo.ClassCreator.close(ClassCreator.java:173)
at io.quarkus.panache.rx.deployment.GizmoTest.main(GizmoTest.java:36)
Which is very hard to explain the cause is the load(0)
which should be a load(0l)
This release will include the new ClassTransformer
API introduced in 3080339.
I would be nice if ClassCreator provides a static API to produce an instance of ClassCreator with try/catch with resource inside API, instead to delegate to a calling function the responsibility to implement try/catch
public static void withClassCreator(ClassCreator.Builder builder, Consumer<ClassCreator> consumer){
try(ClassCreator classCreator = builder.build()){
consumer.accept(classCreator);
}
}
An example of usage
withClassCreator(
ClassCreator.builder()
.classOutput(cl)
.className("com.MyTest")
.interfaces(MyInterface.class),
creator -> {
MethodCreator method = creator.getMethodCreator("transform", String.class, String.class);
ResultHandle existingVal = method.load("complete");
TryBlock tb = method.tryBlock();
tb.addCatch(IllegalStateException.class).load(34);
tb.invokeStaticMethod(MethodDescriptor.ofMethod(ExceptionClass.class.getName(), "throwIllegalState", "V"));
});
Currently, the generated bytecode contains some garbage as described in quarkusio/quarkus#36626 (comment).
We could try to detect that the case block ends with a return
operation and do not break the scope.
In Quarkus, and in particular in ArC, there's the frequent need to generate code to record a Set
of Class constants; for example any created InjectableBean
will have a field private final Set types;
which needs to be initialized in the constructor.
The code being generated today is very verbose and generates intermediate variables for each individual element of the array; for example, focusing on the "type" use case we'd typically have:
Object[] var1 = new Object[2];
Class var2 = someClazz();
var1[0] = var2;
Class var3 = someClazz();
var1[1] = var3;
Set var4 = Set.of(var1);
this.types = var4;
as bytecode:
0: iconst_2
1: anewarray #2 // class java/lang/Object
4: astore_1
5: invokestatic #3 // Method someClazz:()Ljava/lang/Class;
8: astore_2
9: aload_1
10: iconst_0
11: aload_2
12: aastore
13: invokestatic #3 // Method someClazz:()Ljava/lang/Class;
16: astore_3
17: aload_1
18: iconst_1
19: aload_3
20: aastore
21: aload_1
22: invokestatic #4 // InterfaceMethod java/util/Set.of:([Ljava/lang/Object;)Ljava/util/Set;
25: astore 4
27: aload_0
28: aload 4
30: putfield #5 // Field types:Ljava/util/Set;
While this could have been expressed better as:
this.types = Set.of(someClazz(), someClazz());
0: aload_0
1: invokestatic #3 // Method someClazz:()Ljava/lang/Class;
4: invokestatic #3 // Method someClazz:()Ljava/lang/Class;
7: invokestatic #7 // InterfaceMethod java/util/Set.of:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/Set;
10: putfield #5 // Field types:Ljava/util/Set;
There's several minor benefits such ask bytecode size and the ability to skip allocating the array, but most importantly the lack of need for local variables would allow us to save space in the Constant Pool.
It would be nice to introduce some ad-hoc optimisations for these common patterns as required by ArC; we could simplify quite some code by just having specific support for recording immutable Sets and arrays.
I'm suggesting to add both optimisations:
As made clear above, there's many benefits from this and set creation seems to be a very common need - in both ArC and likely other components too.
This should help cover many other usecases; there are cases in which we need to store arrays that get non-trivial in size, and not all of them get folded into a Set. Having ad-hoc optimisations for arrays would give some more flexibility and address the largest consumers of space in the constant table.
Ideally, but probably a much larger effort, it would be nice to see if gizmo could automatically chain directly consumed ResultHandler
(s) which are consumed only once so to not need local variables in such cases but use the stack.
That would have a more pervasive effect in removing many more variables, however seems to require some substantial design changes and I also suspect it wouldn't be able to generate the above optimal bytecode, so I'd focus on the Set and Array optimisations first.
If you create a function with capture with an if
statement inside the method body and attempt to use the captured result handles inside the statement you'll get java.lang.VerifyError: Bad local variable type
.
The generated bytecode looks like this:
Method get : Ljava/lang/Object;
(
// (no arguments)
) {
** label1
** label2
LDC (Boolean) true
IFNE label3
** label4
** label5
GOTO label6
** label3
ALOAD 1
LDC (String) "-func"
// Method descriptor: (Ljava/lang/String;)Ljava/lang/String;
INVOKEVIRTUAL java/lang/String#concat
ARETURN
** label6
ALOAD 0
// Field descriptor: Ljava/lang/String;
GETFIELD com/MyTest$$function$$3#f0
ASTORE 1
ACONST_NULL
ARETURN
** label7
** label8
}
Note that the captured result handle is allocated after IFNE
.
I'll send a PR with the test but I'm not sure how to fix this properly.
At the moment, there is no specific method to implement:
if (x instanceOf y) {
// true branch
} else {
// false branch
}
There is a checkCast
but that's not testing, just doing the cast.
The syntax is quite arcane and is difficult to get right, in particular when needing to call super constructors.
I need to generate methods with Bean Validation annotations, such as foo(@NotEmpty String param)
and if I can't set the method parameter name, then I can't obtain the right validation constraint cause (param
).
The ClassOutput
interface methods don't declare any exceptions, even though they would frequently be used to write a class to disk. They should declare IOException
probably. But of course this will affect source-level compatibility.
Maybe I'm blind but I don't see any support for arithmetic. Specifically I need an increment operation
int x = 0;
x++;
Calling the same sequence of Gizmo methods with the same parameters at different occasions should yeld binary equal byte code.
A PR follows.
ClassCreator
currently calls ASM ClassVisitor with ACC_PUBLIC | ACC_SUPER | ACC_SYNTHETIC | extraAccess
around here:
which is only suitable for common classes, but not interfaces. To make interfaces possible, we need
ACC_INTERFACE
and ACC_ABSTRACT
flagsACC_SUPER
Right now, you need to use signature
passing it a complex string describing how your type is parameterized. It would be nice if that complexity could be hidden away.
Currently, we only have ifNonZero
and ifNull
. It would be useful to add more variants, ifgt
or iflt
at least.
Hi,
Byte Buddy is using ASM9 now; I'd like to update the Byte Buddy used in Hibernate ORM.
Technically the version used by Hibernate via Byte Buddy doesn't have to be the same as Gizmo as ASM is being shaded, but I'd not want too much trouble :)
@stuartwdouglas , WDYT ?
Thanks
While I was trying to generate Microprofile config converters at build time for user-defined classes, I came into struggle about how to specify the generic class when implementing the interface. I tried to following approaches.
try (ClassCreator classCreator = ClassCreator.builder()
.className(converterClassName)
.interfaces(Converter.class)
.classOutput(gizmoAdaptor)
.build()) {
...
}
Unsurprisingly, this wasn't working since the generic wasn't specified anywhere.
After some research, I tried to use the JVM signature directly, which did not lead me to the desired result.
final String s = "Ljava/lang/Object;Lorg/eclipse/microprofile/config/spi/Converter<L" + className.replace(".", "/") + ";>;";
try (ClassCreator classCreator = ClassCreator.builder()
.className(converterClassName)
.signature(s)
.classOutput(gizmoAdaptor)
.build()) {
...
}
With className
as the fully qualified generic class name.
Is there a way the use generics with Gizmo at all? Maybe is there some (internal) documentation about how to use Gizmo?
Hello,
I try to use jandex to add annotation like:
@XmlAccessorType(XmlAccessType.FIELD)
So I use the following code:
classCreator.addAnnotation(AnnotationInstance.create(
DotName.createSimple(XmlAccessorType.class.getName()), null,
new AnnotationValue[]{
AnnotationValue.createEnumValue("value",
DotName.createSimple(XmlAccessType.class.getName()),
String.valueOf(XmlAccessType.FIELD))}));
but I try a lot of different value for string value: "FIELD", String.valueOf(XmlAccessType.FIELD), XmlAccessType.FIELD.toString(), XmlAccessType.FIELD.toString(), XmlAccessType.FIELD.name() but it never work.
AnnotationValue.createStringValue work but impossible to have a string value for a regular enum.
generated class has the annotation but without value:
@XmlAccessorType
public class Reply {
instead of
@XmlAccessorType(XmlAccessType.FIELD)
public class Reply {
Indeed it seems that a code is missing in
https://github.com/quarkusio/gizmo/blob/0a6f1bd53288a76fbe6b28496833f644a96db710/src/main/java/io/quarkus/gizmo/AnnotatedElement.java
to handle Kind.Enum
I think something like
else if (member.kind() == AnnotationValue.Kind.ARRAY) {
Class enumType = (Class) Class.forName(member.asEnumType().toString());
Enum enumval = Enum.valueOf(enumType, member.asEnum());
ac.addValue(member.name(),enumval );
}
This is a source of errors, in particular when creating constructors, as one needs to remember to call returnValue(null)
.
[error]: Build step io.quarkiverse.cxf.deployment.QuarkusCxfProcessor#build threw an exception: java.lang.IllegalArgumentException: Unsupported api 589824
at org.objectweb.asm.ClassVisitor.<init>(ClassVisitor.java:76)
at io.quarkus.gizmo.GizmoClassVisitor.<init>(GizmoClassVisitor.java:22)
when I use
final GizmoClassVisitor cw = new GizmoClassVisitor(Gizmo.ASM_API_VERSION, file,
classOutput.getSourceWriter(postFixedName));
java 8 version seems to be 589824.
I do not understand why Gizmo produce a such error.
This lays groundwork for fixing quarkusio/quarkus#2923.
It should be possible to use the invokeSpecial
instruction to invoke an interface default method. However, BytecodeCreator.invokeSpecialMethod()
always adds an InvokeOperation
with interfaceMethod=false
(constant pool contains Methodref
instead of InterfaceMethodref
).
We could either add a special method, something like BytecodeCreator.invokeSpecialInterfaceMethod()
or add a variant with interfaceMethod
boolean parameter.
I just tripped over a little inconsistency in ResultHandle. The getResultType
method is public:
public ResultType getResultType() {
return this.resultType;
}
... but the ResultType
class is package-private.
I'd submit a PR to either make the method package private or make the type public, but I wasn't sure which was preferable. Maybe there's a reason for it to be package private?
People are frequently tripping over casting, because primitive casts, object casts, and boxing/unboxing are all done in different ways. Provide a smart cast
operation with the following support:
convertPrimitive
checkCast
And optionally:
BoxType.valueOf(...)
BoxType.xxxValue()
followed by primitive-to-primitive conversion (if necessary)Since checkCast
allows a String
argument, primitive types and box types would need to be recognized by name.
Add some basic API usage docs in the README.
the need is to support package annotation.
In cxf patch, I need to support this generation:
https://github.com/apache/cxf/blob/master/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/WrapperClassGenerator.java#L234
Translated into if_acmpeq
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.