How to visit annotation inside method in ASM - java

guys, I can not visit local variable annotation use asm MethodVisitor, I do not know how to do, please help me, I want get the value "in method", but the MethodVisitor visitLocalVariableAnnotation() method does not called by the ASM logic, here is my code:
#Retention(RetentionPolicy.CLASS)
#Target({ElementType.LOCAL_VARIABLE})
public #interface SendEvent {
String value() default "hello every";
}
public class AnnotationTest {
public void test() {
#SendEvent(value = "in method")
EventBase base = new EventBase();
}
}
public class AsmMethodVisitor extends MethodVisitor implements Opcodes {
public AsmMethodVisitor(MethodVisitor methodVisitor) {
super(ASM7, methodVisitor);
System.out.println("== AsmMethodVisitor");
}
#Override
public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start, Label[] end, int[] index, String descriptor, boolean visible) {
System.out.println("== visitLocalVariableAnnotation");
return super.visitLocalVariableAnnotation(typeRef, typePath, start, end, index, descriptor, visible);
}
}

This is expected, as per JLS 9.6.4.2 this information is simply not retained in the class file:
9.6.4.2. #Retention
Annotations may be present only in source code, or they may be present in the binary form of a class or interface. An annotation that is present in the binary form may or may not be available at run time via the reflection libraries of the Java SE Platform. The annotation type java.lang.annotation.Retention is used to choose among these possibilities.
If an annotation a corresponds to a type T, and T has a (meta-)annotation m that corresponds to java.lang.annotation.Retention, then:
If m has an element whose value is java.lang.annotation.RetentionPolicy.SOURCE, then a Java compiler must ensure that a is not present in the binary representation of the class or interface in which a appears.
If m has an element whose value is java.lang.annotation.RetentionPolicy.CLASS or java.lang.annotation.RetentionPolicy.RUNTIME, then a Java compiler must ensure that a is represented in the binary representation of the class or interface in which a appears, unless a annotates a local variable declaration or a annotates a formal parameter declaration of a lambda expression.
An annotation on the declaration of a local variable, or on the declaration of a formal parameter of a lambda expression, is never retained in the binary representation. In contrast, an annotation on the type of a local variable, or on the type of a formal parameter of a lambda expression, is retained in the binary representation if the annotation type specifies a suitable retention policy.
Note that it is not illegal for an annotation type to be meta-annotated with #Target(java.lang.annotation.ElementType.LOCAL_VARIABLE) and #Retention(java.lang.annotation.RetentionPolicy.CLASS) or #Retention(java.lang.annotation.RetentionPolicy.RUNTIME).
If m has an element whose value is java.lang.annotation.RetentionPolicy.RUNTIME, the reflection libraries of the Java SE Platform must make a available at run time.
If T does not have a (meta-)annotation m that corresponds to java.lang.annotation.Retention, then a Java compiler must treat T as if it does have such a meta-annotation m with an element whose value is java.lang.annotation.RetentionPolicy.CLASS.
(emphasis mine)
ASM can simply not emit events for information that is not present.

Related

What is the meaning of defining enums and fields inside annotation declarations?

It seems it is possible to declare fields and enums inside the declaration of an annotation in Java. For example, javac compiles this:
#interface ClassPreamble {
public enum AnEnum {
Value;
}
String aField = "";
String author();
String date();
String currentRevision() default "";
String lastModified() default "N/A";
String lastModifiedBy() default "N/A";
// Note use of array
String[] reviewers();
}
What is the meaning / usefulness of defining enums and fields inside annotation declarations?
Thanks
Java annotations are just interfaces that inherit java.lang.annotation.Annotation (*), and are treated specially by the compiler to a certain extent, but the compiler will not prevent you from doing whatever is legal within interfaces. It can even be useful (see example from JLS at bottom of this answer).
(*) Although an interface that manually extends Annotation does not define an annotation type (source: javadoc for Annotation.java)
Your annotation ClassPreamble is effectively interface ClassPreamble extends java.lang.annotation.Annotation (decompile it to take a look).
It is legal to declare an enum within an interface.
It is legal to declare a field in an interface: it will be implicitly public static final.
$ cat Funny.java
interface Funny {
public enum AnEnum { One, Two }
String aField = "";
}
$ javac Funny.java
$ javap -c Funny.class
Compiled from "Funny.java"
interface Funny extends java.lang.annotation.Annotation {
public static final java.lang.String aField;
}
$ javap -c Funny\$AnEnum.class | head
Compiled from "Funny.java"
public final class Funny$AnEnum extends java.lang.Enum<Funny$AnEnum> {
public static final Funny$AnEnum One;
public static final Funny$AnEnum Two;
public static Funny$AnEnum[] values();
Code:
0: getstatic #1 // Field $VALUES:[LFunny$AnEnum;
3: invokevirtual #2 // Method "[LFunny$AnEnum;".clone:()Ljava/lang/Object;
...
I'm not sure there is a good answer for "what is the meaning of such constructs?". I guess it was implemented this way as a design choice: whatever the annotation does is encoded as bytecode for an interface, which the JVM knows how to deal with, so they did not need to modify the JVM itself too heavily (if at all), it allowed the desired features for compile-time and for run-time, and it does not do harm (or does it?).
Edit: 2 extracts from the Java Language Specification section 9.6 about annotations (which belongs to chapter 9 about interfaces):
An annotation type declaration specifies a new annotation type, a special kind of interface type. To distinguish an annotation type declaration from a normal interface declaration, the keyword interface is preceded by an at-sign (#).
[...]
The grammar for annotation type declarations permits other element declarations besides method declarations. For example, one might choose to declare a nested enum for use in conjunction with an annotation type:
#interface Quality {
enum Level { BAD, INDIFFERENT, GOOD }
Level value();
}

How to prune variations of same method obtained by use of Java reflection?

I'm using reflection discover a method satisfying some conditions and to invoke the found method.
Check following code. Using Groovy..
class TestClass<T>{
T hello(){
return null
}
}
class TestSubClass extends TestClass<List<String>>{
List<String> hello(){
return null
}
}
TestSubClass.methods.each{
if(it.name.contains("hello")){
println it.toGenericString()
}
}
which prints out
public java.util.List<java.lang.String> TestSubClass.hello() // <-- most relevant method for a user of this class
public java.lang.Object TestSubClass.hello()
public java.lang.Object TestSubClass.super$2$hello()
Java reflection is returning multiple declarations of same method based on inheritance/generics, which is understandable.
In my case, I'd like to discover the method with most appropriate signature, including exact type of returnTypes. For example, in the above example, the 1st method in the output has full signature and that's the one we'd usually invoke (without reflection).
Note: above is a simplified example. The real logic is not about finding methods based on naming.
The compiler generates the other 2 methods. Luckily, there is a property that you can check to see this: synthetic:
TestSubClass.declaredMethods.each{
if(it.name.contains("hello") && !it.synthetic) {
println it.toGenericString()
}
}
Which now prints just:
public java.util.List<java.lang.String> test.TestSubClass.hello()
The Java specifications require a method to marked synthetic if it is not explicitly in the source code.
A construct emitted by a Java compiler must be marked as synthetic if
it does not correspond to a construct declared explicitly or
implicitly in source code, unless the emitted construct is a class
initialization method (JVMS §2.9).
JAVA specifications
You can try:
TestSubClass.methods.each{
if(it.name.contains("hello") && !m.isSynthetic()){
println it
}
}
You can also check against if the method is bridged. Which is a similar concept:
https://stackoverflow.com/a/5007394/1754020
In my case, I'd like to discover the method with most appropriate
signature, including exact type of return Types.
If it's the Java API that you're wondering about, then you'll want to look at class Class. It contains a large number of reflective methods that allow you to interrogate types.
For example, the following code fragment searches all the methods declared on a supplied type for one method which: takes no arguments, is public and static, and has a return type of DateSerial.Config...
public static <D extends DateSerial<?>> DateSerial.Config<D> obtainMetadata(Class<D> cls) {
Method exe = Stream.of(cls.getDeclaredMethods())
.filter(m -> m.getParameterCount() == 0 &&
m.getReturnType() == DateSerial.Config.class)
.filter(m -> {
int mod = m.getModifiers();
return Modifier.isStatic(mod) && Modifier.isPublic(mod);
})
.findFirst()
.orElseThrow(() -> new IllegalArgumentException(
"No metadata accessor for " + cls.getName()));
:
:
}
You can get as precise with your interrogations as you need. For example, you can filter methods based on those with a certain number of arguments, the last of which is a String[] array, etc. etc. Caveat emptor: Java reflective code is verbose, ugly, and can be hard to read.

Java method reference to a method with generic parameter

I'm trying to make a method reference to a method which have a generic parameter specified in a class declaration.
So I have:
public interface IExecutable<P extends IParameter> {
void execute(P parameter);
}
public class Parameter implements IParameter {
public void childSpecific() {
...
}
}
public class TestClass {
...
//somewhere in the code
public void foo(Parameter parameter) {
parameter.childSpecific();
}
public void test() {
IExecutable<?> executable = this::foo; //compilation error
// The type TestClass does not define inner(IParameter) that is applicable here
executable.execute(new Parameter()); //compilation error as well
// The method execute(capture#4-of ?) in the type IExecutable<capture#4-of ?> is not applicable for the arguments (Parameter)
}
...
}
It's specific that I don't know the concrete generic type of the executable here. Using
IExecutable<Parameter> = ...
solves the problem immediately, but it's impossible for the case.
Clearly, I'm doing something wrong. But how to make it work?
Thx.
In this case, foo is not written to handle any IParameter other than Parameter. You could assign a reference to foo to a variable of type IExecutable<? extends IParameter>, however this means that it is an executable that handles some unknown type of IParameter (in this case, Parameter). Since the specific subtype is unknown, it would not be syntactically safe to pass any subtype of IParameter in to its execute method, since you don't know which it can handle within this scope!
What you need is another type variable instead of using a capture (the ?). This way you can specify that the IParameter you're passing in is the same type as the IParameter the executable accepts. You could introduce this with a new method, like I'm doing below:
public class TestClass {
public static void foo(Parameter parameter) {
parameter.childSpecific();
}
public static void main(String args) {
execute(TestClass::foo, new Parameter());
}
public static <P extends IParameter> void execute(
IExecutable<P> executable, P param) {
executable.execute(param);
}
}
The type parameter P in your interface IExecutable is constrained to being a subtype of IParameter. Consider these two subtypes:
class Parameter implements IParameter { ... }
class AnotherParameter implements IParameter { ... }
Now, an IExecutable<?> is not more specific regarding the above mentioned constraint. In fact, the ? states that it is bound to an unknown subtype of IParameter, which could be Parameter or AnotherParameter (in my example).
With such a variable declaration, you face the two problems you mentioned.
Your method foo(Parameter) does not match the more general constraint of an IExecutable<?>. As seen above, such an executable could be bound to AnotherParameter which clearly would violate the method signature of foo.
Even if it matched, it cannot be used like you did. The compiler does not know to which type the ? actually was mapped. The only thing it knows: It must be a subtype of IParameter, but which one is not known. That means, the statement executable.execute(new Parameter()) is not allowed (as also executable.execute(new AnotherParameter())). The only parameter you are allowed to pass to execute is null.
Conclusion: Point 1 could be solved by declaring the variable executable with type IExecutable<? extends Parameter>. This matches the method signature of foo. But point 2 still does not allow the call to execute.
The only thing you can do is to declare the variable as
IExecutable<Parameter> executable = this::foo;
This will compile and allow the call to
executable.execute(new Parameter());
This line exposes failure in java type inference
IExecutable<?> executable = this::foo;
Let's look at it this way
IExecutable<?> executable = p->this.foo(p);
To compile it, java needs to know the meaning of foo(p). Before java8, the type of an expression is built on the types of sub-expressions; here, the type of p needs to be known 1st to resolve foo. But the type of p is not specified, it needs to be inferred from surrounding context. Here the context is IExecutable<? extends IParameter>, and p is inferred to IParameter - and method foo(Iparameter) does not exist.
In general, type inference faces a dilemma, does it infer from top down, or bottom up? Java8 defines an extremely complicated procedure for that, which is humanly impossible to understand:)
Workarounds: specify the type of p
IExecutable<?> executable = (Parameter p)->this.foo(p);
or specify a more specific target type
IExecutable<?> executable = (IExecutable<Parameter>)p->this.foo(p);
IExecutable<?> executable = (IExecutable<Parameter>)this::foo;
If you ask the language designers, they'd consider all of this is quite obvious ... but a programmer's best action is probably just try different things till it works, than to study the actual language spec.

Why java annotation attributes have restrictions?

I noticed that if I create an annotation:
public #interface NullableTypeOverride {
NullableType hibernateTypeOverride();
}
I have limited options for annotation attributes. The above code will not work because annotations only take primitive, String or Class types for their attributes.
So in this case I can't use this annotation like this:
#NullableTypeOverride(hibernateTypeOverride = Hibernate.INTEGER)
private Long distance;
My guess is that it has something to do with compile time vs. runtime but I'm not entirely sure. So what is the reason for this limitation and how can I work around it?
The JLS states
It is a compile-time error if the return type of a method declared in
an annotation type is not one of the following: a primitive type,
String, Class, any parameterized invocation of Class, an enum type
(§8.9), an annotation type, or an array type (§10) whose element type
is one of the preceding types.
The reason for this is that annotations must have a constant value. If you provide a reference to an object that may change, you'll have problems. This is only relevant if the annotation's Retention is RUNTIME.
public class Person {
public String name;
}
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {
Person person();
}
#MyAnnotation(person = ???) // how to guarantee it won't change at runtime?
public void method1() {...}
What's that value supposed to be? And how can reflection libs cache it?
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
annotation.person(); // should be the same value every time
Remember, annotations are supposed to be metadata.

Why The value for annotation attribute Rest.rootUrl must be a constant expression?

Im using Android Annotations Framework, specially for Rest Integration.
I have the following code.
An interface for Host configuration
public interface Host {
public String URL = "http://192.168.2.137";
}
And the annotated Interface for Rest communication.
#Rest(rootUrl = Host.URL, converters = { MappingJacksonHttpMessageConverter.class })
public interface RestClient {
#Get("/entities.json")
Entity[] allEntities();
}
and my question is, Why the value for annotation attribute Rest.rootUrl must be a constant expression? and how can i use a String resource for Rest.rootUrl ?
I wish to do something like
#EBean
public class Host{
#StringRes
String URL;
}
But is impossible with the RestClient interface.
The idea is to handle a localized rest application, suppose distinct URLs by language
http://en.myapp.com
http://es.myapp.com
I know that an Java Interface must have final properties, but, there are a way to handle a localized rootUrl value?
Thanks.
Jon is right about annotation values, but Android Annotations actually does give you a way to dynamically set the root url for a RestClient.
Just omit the rootUrl attribute from the annotation and add a method to the interface:
void setRootUrl(String rootUrl);
Just remember that you'll need to call RestClient.setRootUrl(url) at some point in your app before you actually use RestClient.
More info at https://github.com/excilys/androidannotations/wiki/Rest%20API#rest
Why the value for annotation attribute Rest.rootUrl must be a constant expression?
This isn't really an Android question in particular, or about those specific annotations. All annotation values in Java have to be constant expressions - because those values are baked into the classfile at compilation time.
From the JLS section 9.7:
An element type T is commensurate with an element value V if and only if one of the following conditions is true:
T is an array type E[] and either:
V is an ElementValueArrayInitializer and each ElementValue (analogous to a VariableInitializer in an array initializer) in V is commensurate with E; or
V is an ElementValue that is commensurate with E.
The type of V is assignment compatible (§5.2) with T, and furthermore:
If T is a primitive type or String, and V is a constant expression (§15.28).
V is not null.
If T is Class, or an invocation of Class, and V is a class literal (§15.8.2).
If T is an enum type, and V is an enum constant.

Categories

Resources