Why is the SIZE constant only @Native for Integer and Long?

java, java-8

Solution

TLDR: Jump to the conclusion

Why is the SIZE constant only @Native for Integer and Long?

A brief history of `@Native`

I made some search on the mailing lists. I found some interesting things.

At first an annotation (1 2) `javax.tools.annotation.ForceNativeHeader` was introduced to

to trigger javah on a class.

It is used by `com.sun.tools.javac.processing.NativeapiVisitor`. By looking at the code we can see that the native header is generated if the class declare some native methods or if the class is annotated `@ForceNativeHeader`.

Later this annotation was renamed to `GenerateNativeHeader` (1 2).

Then this annotation was added to several types (especially Integer and Long) with an interresting comment:

/* No native methods here, but the constants are needed in the supporting JNI code */
@GenerateNativeHeader
public final class Long extends Number implements Comparable<Long> {...

But by adding this annotation it add a problematic dependency from base module to the module containing javax.tools. So the annotation were removed from `Integer` and `Long` and these files were explicitly added to the build process since the header were no more automatically generated... a "(hopefully temporary) hack".

So a new annotation `java.lang.annotation.Native` was created and used in Integer and Long. The annotation was set a `TargetType FIELD`.

the annotation should be directly applied to the constant fields that need to be exported -- and not to the class as a whole.

All the purpose of this stuff is:

javac could generate native headers for classes containing native methods.

It is the case of `Integer` and `Long`

this was a part of the JEP 139: Enhance javac to Improve Build Speed:

javah will be automatically run on any class that contains native methods and the generated C-headers will be put in the (-h) headerdir. A new annotation @ForceNativeHeader is used for classes that have final static primitives that need to be exported to JNI, but no native methods.

A basic experimentation

I made a basic experimentation on the JDK. I clone the open-jdk forest and i successfully build it. As expected the header files where generated for `Integer` and `Long` (thanks to `@Native`) and for `Float` and `Double` (thanks to their native methods) but not for `Byte`, `Short`...

    ls -l build/macosx-x86_64-normal-server-release/support/headers/java.base/java_lang_*
    ...
    java_lang_Double.h
    java_lang_Float.h
    java_lang_Integer.h
    java_lang_Long.h
    java_lang_Object.h
    java_lang_Package.h
    ...
    

Then i tried to remove the `@Native` from the `Integer` fields and i tried to build again the `jdk` but i get an error:

jdk/src/java.base/unix/native/libnio/ch/FileChannelImpl.c:35:10: fatal error: 'java_lang_Integer.h' file not found
#include "java_lang_Integer.h"
         ^
1 error generated.

logically since the header have not been generated.

I have also confirmed that `java_lang_Integer.h` is included in several c and cpp files:

find .  \( -name "*.c" -o -name "*.cpp" \) -exec grep "java_lang_Integer.h" {} \; -print
#include "java_lang_Integer.h"
./jdk/src/java.base/unix/native/libnio/ch/FileChannelImpl.c
#include "java_lang_Integer.h"
./jdk/src/java.base/unix/native/libnio/ch/IOUtil.c
#include "java_lang_Integer.h"
./jdk/src/java.base/windows/native/libnet/TwoStacksPlainSocketImpl.c
#include "java_lang_Integer.h"
./jdk/src/java.base/windows/native/libnio/ch/FileChannelImpl.c
#include <java_lang_Integer.h>
./jdk/src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp

like `Long`

find .  \( -name "*.c" -o -name "*.cpp" \) -exec grep "java_lang_Long.h" {} \; -print
#include "java_lang_Long.h"
./jdk/src/java.base/unix/native/libnio/ch/FileDispatcherImpl.c

like `Float`

find .  \( -name "*.c" -o -name "*.cpp" \) -exec grep "java_lang_Float.h" {} \; -print
#include "java_lang_Float.h"
./jdk/src/java.base/share/native/libjava/Float.c
#include "java_lang_Float.h"
./jdk/src/java.base/share/native/libjava/ObjectInputStream.c
#include "java_lang_Float.h"
./jdk/src/java.base/share/native/libjava/ObjectOutputStream.c

and like `Double`

find .  \( -name "*.c" -o -name "*.cpp" \) -exec grep "java_lang_Double.h" {} \; -print
#include "java_lang_Double.h"
./jdk/src/java.base/share/native/libjava/Double.c
#include "java_lang_Double.h"
./jdk/src/java.base/share/native/libjava/ObjectInputStream.c
#include "java_lang_Double.h"
./jdk/src/java.base/share/native/libjava/ObjectOutputStream.c

but neither `Short`

find .  \( -name "*.c" -o -name "*.cpp" \) -exec grep "java_lang_Short.h" {} \; -print

nor `Byte`, nor `Character`.

Conclusion

Among all these types, only `Integer`, `Long`, `Float`, `Double` are used in the native source code of the jdk.

And only the `Integer` and `Long` fields are annotated with `@Native` because they have no native methods (as opposed to `Float` and `Double`)

Problem

I understand the use of the `@Native` annotation. Indicates that a field defining a constant value may be referenced from native code. The annotation may be used as a hint by tools that generate native header files to determine whether a header file is required, and if so, what declarations it should contain. However, while reading the java source code I noticed that in the class `Integer` and `Long` the `SIZE` constant is `@Native` while it is not for the Float, Byte, Double, Short and Character. Note that the SIZE constant represents the number of bits used to represent the actual value. ``` public static final int SIZE = 8;//Byte public static final int SIZE = 16;//Character public static final int SIZE = 16;//Short public static final int SIZE = 32;//Float @Native public static final int SIZE = 32;//Integer @Native public static final int SIZE = 64;//Long public static final int SIZE = 64;//Double ``` Edit : I just noticed that this also applies for `MAX_VALUE` and `MIN_VALUE` of the same classes. Edit 2 : I had spare time to do some research on this, and looking at the header files of the Long, Float etc. classes I had hope to figure out that the constants were not present in the other headers, but unfortunately they are. ``` static const jint SIZE = 8L;//java/lang/Byte.h static const jint SIZE = 16L;//java/lang/Character.h static const jint SIZE = 16L;//java/lang/Short.h static const jint SIZE = 32L;//java/lang/Float.h static const jint SIZE = 32L;//java/lang/Integer.h static const jint SIZE = 64L;//java/lang/Double.h static const jint SIZE = 64L;//java/lang/Long.h ``` Why is the SIZE constant only @Native for Integer and Long ?

Original source