Java Bytecode Signatures - java

As part of the compiler for the programming language I am working on, I came across generic signatures in the bytecode, which I am trying to parse and convert to an AST. The parsing algorithm mostly works, but there seems to be a special case in which the format of these signatures behaves a bit strangely. Here are a few of these cases:
java.util.Arrays#parallelSort: <T::Ljava/lang/Comparable<-TT;>;>([TT;)V
java.util.Arrays#parallelSort: <T::Ljava/lang/Comparable<-TT;>;>([TT;II)V
java.lang.Class#getAnnotation: <A::Ljava/lang/annotation/Annotation;>(Ljava/lang/Class<TA;>;)TA;
java.lang.Class#getAnnotationsByType: <A::Ljava/lang/annotation/Annotation;>(Ljava/lang/Class<TA;>;)[TA;
java.lang.Class#getDeclaredAnnotation: <A::Ljava/lang/annotation/Annotation;>(Ljava/lang/Class<TA;>;)TA;
java.lang.Class#getDeclaredAnnotationsByType: <A::Ljava/lang/annotation/Annotation;>(Ljava/lang/Class<TA;>;)[TA;
java.util.Arrays#parallelSort: <T::Ljava/lang/Comparable<-TT;>;>([TT;)V
java.util.Arrays#parallelSort: <T::Ljava/lang/Comparable<-TT;>;>([TT;II)V
java.util.Collections#sort: <T::Ljava/lang/Comparable<-TT;>;>(Ljava/util/List<TT;>;)V
Out of all the methods in these classes, these are the only ones that have :: in their signature. My question is what this token does and why it exists.
Edit
I know about the :: operator in the Java Language, but this is something on the Bytecode level.

There is a defined syntax that changed as of JSR 14 to specify the bounds of a generic type.
variable_name:class_type_bound:interface_type_bounds
So for your example of:
<T::Ljava/lang/Comparable<-TT;>;>
Which would reflect:
<T extends Comparable<T>>
The variable name is T, there is no class type bound so it was omitted, and there was an interface bound of type Comparable<T>.
All your example follow this, but there any many different forms:
<T:Ljava/lang/Object;>(Ljava/util/Collection<TT;>;)TT;
<T::Ljava/lang/Comparable;>(Ljava/util/Collection<TT;>;)TT;
<T:Ljava/lang/Object;:Ljava/lang/Comparable;(Ljava/util/Collection<TT;>;)TT;
Source

Related

Why is second level generics not possible in Java

Long story short:
Why is the following not possible in Java?
public class Test<A<B>> {} // A and B both being generic parameters.
Note: I don't have any specific use case right now, rather I am just trying to understand why this is not allowed.
At first I thought because the compiler cannot assert if A can accept generics parameter because after compiling A, due to type erasure the generics won't be present anymore.
But, if that is the case, then we cannot use generics on any class at all. So I took out the byte code of a generic class and found that there is metadata to say it accepts generics.
public class com.Test<T> {
public com.Test();
Code:
0: aload_0
1: invokespecial #12 // Method java/lang/Object."<init>":()V
4: return
}
And I did a quick search and SO confirmed compiled code will have generics related metadata too
So why does the compiler not allow multilevel generics?
Will there be any problem in allowing it? Is it a limitation? or something else?
Let's assume that this class was actually compiling:
public class Test<A<B>> { .. }
This implies that a proper instantiation of the class would be:
new Test<Class<Integer>>()
//or
new Test<List<String>>()
and the following wouldn't be correct (since the provided type-parameter is not generic):
new Test<String>();
//or
new Test<Object>();
However, a type-parameter should not be restricted for being generic or not - it should just hold some meta information about the type (and as it turns out, this is the type after type-erasure has taken place) with which it would be replaced.
Type-erasure itself can be another possible reason for not allowing such constructions. Let's again consider the above Test class was correctly defined and you had this:
new Test<Class<Integer>>();
When type-erasure happens, <A<B>> should be replaced with Class<Integer>, however, due to the erasure, we'd have only a Class (even though internally it would contain info about the Integer type) - expecting a Class<Integer>, but providing a Class should not be correct.
Thanks for the interesting question!
Scala calls it higher kinded types. So it's definitely a feasible abstraction. Adding it to Java has — to my knowledge — never been seriously considered.
Sadly, I can't find any good introductory text to Scala's higher kinded types. The best I can find is the original paper Generics of a Higher Kind; here's its abstract:
With Java 5 and C# 2.0, first-order parametric polymorphism was introduced in mainstream object-oriented programming languages under the name of generics. Although the first-order variant of generics is very useful, it also imposes some restrictions: it is possible to abstract over a type, but the resulting type constructor cannot be abstracted over. This can lead to code duplication. We removed this restriction in Scala, by allowing type constructors as type parameters and abstract type members. This paper presents the design and implementation of the resulting type constructor polymorphism. Furthermore, we study how this feature interacts with existing object-oriented constructs, and show how it makes the language more expressive.

Should I use question mark type argument or suppress rawtypes warning?

Since the introduction of generic type parameters (or type arguments) in Java, writing the following line in Eclipse IDE will show a yellow squiggly line on the type Class:
Class myClass;
The warning shown on a mouseover is the following, with the two options (among others):
Class is a raw type. References to the generic type Class<T> should be parameterized.
Add type arguments to 'Class'
Add #SuppressWarnings 'rawtypes' to 'myClass'
The first option produces this code:
Class<?> myClass;
The second one produces this:
#SuppressWarnings("rawtypes")
Class myClass;
Both of the above options are equally adequate in taking care of the warning.
Assuming neither we nor Eclipse can infer generic type arguments*; what is the better approach to take, in what situations, and why?
* (despite having the option to try to do so)
A general rule every programmer should adhere to: never suppress warnings for no good reason. If you suppress a warning, you should know why the warning occurs and why it does not present a problem in your case.
In this case the reason to suppress a rawtype warning would be to support legacy code that was written in a time when genericity did not exist in Java (See the Java Language Specification 4.8):
The use of raw types is allowed only as a concession to compatibility
of legacy code. The use of raw types in code written after the
introduction of genericity into the Java programming language is
strongly discouraged. It is possible that future versions of the Java
programming language will disallow the use of raw types.
So it does not apply to your case, you should not suppress this warning.
As to the Wildcard ?, this should only be used if you want to assign various types to that variable.
For example a List<?> mylist lets you store any type in the list, while List<? extends Collection> lets you store any type that inherits from Collection to be stored in the list. A general rule is to be as specific about allowed types as possible. This way the compiler can notify you sonner if you accidentally add an object into a List that is not meant for it.
So the best way to go here is to think about how you want to use your myClass variable and if you can make a statement about which type of objects it will hold, replace the Wildcard with that type.

Why I'm not required to specify a type argument when using a Generic class?

I was surprised today when this code compiled:
class GenericClass<T> {
public void emptyMethod(T instance) {
// ..
}
public void print(T instance) {
System.out.println(instance);
}
}
public class Main {
public static void main(String[] args) {
GenericClass first = new GenericClass();
System.out.println("Wow");
first.emptyMethod(10);
first.print(16);
}
}
The compiler emits a warning (Type safety: The method emptyMethod(Object) belongs to the raw type GenericList. References to generic type GenericList should be parameterized), but anyway it does not cause a compiler error and it runs 'fine' (at least the provided print method). As I'm understanding, the compiler is using object as the type argument, but I find it counter-intuitive. Why would the compiler do such thing? Why it doesn't require me to specify the type parameter?
You're using a raw class, basically.
Think back to when generics were first introduced in Java: there was a load of code which already used List, ArrayList etc. In order to avoid breaking all of that code, but still reusing the existing classes, raw types were introduced - it's basically using a generic type as if it weren't one.
As you can see, you get a warning - so it's worth avoiding - but that's the primary reason for it being allowed at all.
See section 4.8 of the JLS for more information, which includes:
Raw types are closely related to wildcards. Both are based on existential types. Raw types can be thought of as wildcards whose type rules are deliberately unsound, to accommodate interaction with legacy code. Historically, raw types preceded wildcards; they were first introduced in GJ, and described in the paper Making the future safe for the past: Adding Genericity to the Java Programming Language by Gilad Bracha, Martin Odersky, David Stoutamire, and Philip Wadler, in Proceedings of the ACM Conference on Object-Oriented Programming, Systems, Languages and Applications (OOPSLA 98), October 1998.
You have to know how generics are implemented in Java. They are far from perfect.
You have to remember that during run time everything is an Object. There are no types during run time.
Generics were added for added security in places, where you need it, but if you don't want to use it, you can ignore warnings and use unparametrized instances.
However, if you'd like java compiler to help you with type safety, then you parametrize generic class instances. Once you create a GenericClass for example, compiler will not allow you to use it with an integer parameter (first.emptyMethod(10) will not compile). You can still make it work with integer parameter if you do explicit type casting though.
So consider it a good practice for added security, which only works if you follow the rules.

What mechanisms does Scala have for generics and wildcards compared to Java?

I am frequently pushing the limits of Java's type system through my use of Guice, TypeLiteral, generics, and wildcards. I often run into situations where I need to perform unchecked casts, which pretty much ruins type safety--in other words, "Generics Hell."
Here's a simplified example of some of my problematic Java code.
class SquareDrawer implements ShapeDrawer<Row<Square>> {}
class Client {
Key<SquareDrawer> SQUARE_DRAWER_KEY =
Key.get(SquareDrawer.class, randomAnnotation());
void bindShapeDrawer(
Key<? extends ShapeDrawer<Row<? extends Shape>>> shapeDrawer) {}
Client() {
// Note Unchecked cast required below
bindShapeDrawer(
(Key<? extends ShapeDrawer<Row<? extends Shape>>>) SQUARE_DRAWER_KEY);
}
}
I've been learning Scala and have been under the impression (or illusion) that it has better support for generics than Java. Could the above code be written in Scala to avoid the unchecked casts?
Is there still a need for Guice's TypeLiteral in Scala?
There are a couple of things Scala offers.
Higher kinded types (I hope I use the term correctly) allow you to define things like 'any type having another type as a type parameter' afaik there is no way to express that in java
Co and Contravariant type parameters. In java you can make parameters one or the other by using wildcards in every place where they are used. In Scala you just declare them as such.
Type witnesses (Again: Is that the correct term?) are implicit functions that demonstrate some property of type arguments, thereby defining constraints on the type. If an implicit conversion matching the declaration of the witness exists, the call will compile the condition holds.
Path dependent types. You can have types that are elements of instances, so each instance has its own type. Again you can't do this in java afaik.
Scala has a form of reified types called Manifests. They let you do things that would be quite clunky in Java due to type erasure. Read all about them here: http://www.scala-blogs.org/2008/10/manifests-reified-types.html
The following (hopefully) equivalent Scala code compiles without errors. Maybe I need to state that it contains no dynamic casts. Note that I had to make Key covariant in its type argument, because SquareDrawer is only a subtype of ShapeDrawer[Row[Square]].
trait ShapeDrawer[A]
trait Row[A]
trait Shape
trait Square extends Shape
trait Key[+A]
//your code starts here
trait SquareDrawer extends ShapeDrawer[Row[Square]]
class Client{
val SDK = new Key[SquareDrawer]{}
bindShapeDrawer(SDK)
def bindShapeDrawer[SD[A] <: ShapeDrawer[A],S <: Shape](shapeDrawer: Key[SD[Row[S]]]) {}
}

How do I reconstruct generic type information for classes given a TypeLiteral?

I have the following problem:
Given a Guice type literal TypeLiteral<T> template and a class Class c implementing or extending T, construct a type Type t which is equivalent to c with all type variables instantiated so as to be compatible with template.
If c has no type variables, it's easy; c is the type in question. However, if c has type variables, then I need to do the following:
Find the type in c's inheritance and implementation hierarchy corresponding to the raw type of T
Walk through the type parameter structure, finding any type variable uses and their corresponding types in template
Use the Guice Types helper functions to create a type from c instantiated with the types found in (2).
Of course, there are error cases and it might not be complete. If it can't find matching uses of all type variables, it will fail. There might be other cases as well. However, if I have this:
class CS<I> implements S<Map<I,Float>> {
// some stuff
}
and a type literal TypeLiteral<S<Map<I,Float>>>, I want to get a type which represents CS fully instantiated to match the type literal.
It looks like reflection provides enough information to accomplish this, but the logic looks complex and error-prone. Is there an existing library which exposes this logic?
TypeLiteral.getSupertype() should do the trick:
TypeLiteral<?> t = TypeLiteral.get(x).getSupertype(y);
This problem is an instance of the unification problem, and as such the standard unification algorithm is applicable and not as complicated as I initially thought. Further, this instance of the problem allows for some significant simplifying assumptions, as one of the trees will contain no variables. 200 lines of Java later, I have a working solution.

Categories