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 ?