问题描述
If a bean contains a boolean field, it can't be deserialized with false value in GraalVM native image.
环境信息
- OS信息: macOS 14.7 (23H124)
- JDK信息: Java(TM) SE Runtime Environment Oracle GraalVM 17.0.12+8.1 (build 17.0.12+8-LTS-jvmci-23.0-b41)
- 版本信息:2.0.53
重现步骤
I'd created a demo repo in: https://github.com/suragreat/fastjson2-native.
To run:
mvn clean package -Pnative
./target/my-app
public class App {
public static void main(String[] args) throws Exception {
String json = """
{"height":800,"size":"LARGE","title":"image","transparent":"false","uri":"file://image.png","width":600}
""";
System.out.println("original json: " + json);
Image image = JSON.parseObject(json, Image.class);
System.out.println("image from original json: " + image);
String jsonString = JSON.toJSONString(image);
System.out.println("toJSONString: " + jsonString);
image = JSON.parseObject(jsonString, Image.class);
System.out.println("image image from toJSONString: " + image);
}
}
@com.alibaba.fastjson2.annotation.JSONCompiled
public class Image
implements java.io.Serializable {
private int height;
private Size size;
private String title;
private String uri;
private int width;
private boolean transparent;
public Image() {
}
public void setUri(String uri) {
this.uri = uri;
}
public void setTitle(String title) {
this.title = title;
}
public void setWidth(int width) {
this.width = width;
}
public void setHeight(int height) {
this.height = height;
}
public void setSize(Size size) {
this.size = size;
}
public String getUri() {
return uri;
}
public String getTitle() {
return title;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public Size getSize() {
return size;
}
public enum Size {
SMALL, LARGE
}
public boolean isTransparent() {
return transparent;
}
public void setTransparent(boolean transparent) {
this.transparent = transparent;
}
}
期待的正确结果
Deserialized from json string successfully.
相关日志输出
original json: {"height":800,"size":"LARGE","title":"image","transparent":"false","uri":"file://image.png","width":600}
image from original json: io.suragreat.issue.fastjson2.graalvm_native.vo.Image@3c49d008
toJSONString: {"height":800,"size":"LARGE","title":"image","transparent":false,"uri":"file://image.png","width":600}
Exception in thread "main" com.alibaba.fastjson2.JSONException: syntax error : 102
at com.alibaba.fastjson2.JSONReaderUTF8.readBoolValue(JSONReaderUTF8.java:7550)
at com.alibaba.fastjson2.reader.FieldReaderBoolValueMethod.readFieldValue(FieldReaderBoolValueMethod.java:26)
at com.alibaba.fastjson2.reader.ObjectReader6.readObject(ObjectReader6.java:389)
at com.alibaba.fastjson2.JSON.parseObject(JSON.java:864)
at io.suragreat.issue.fastjson2.graalvm_native.App.main(App.java:18)
Root Cause
The root cause is incorrectly Using Unsafe safely in GraalVM Native Image. From the native image building info, there're some warning listed as below:
Warning: RecomputeFieldValue.ArrayBaseOffset automatic substitution failed. The automatic substitution registration was attempted because a call to jdk.internal.misc.Unsafe.arrayBaseOffset(Class) was detected in the static initializer of com.alibaba.fastjson2.util.JDKUtils. Detailed failure reason(s): Could not determine the field where the value produced by the call to jdk.internal.misc.Unsafe.arrayBaseOffset(Class) for the array base offset computation is stored. The call is not directly followed by a field store or by a sign extend node followed directly by a field store.
Warning: RecomputeFieldValue.FieldOffset automatic substitution failed. The automatic substitution registration was attempted because a call to sun.misc.Unsafe.objectFieldOffset(Field) was detected in the static initializer of com.alibaba.fastjson2.util.JDKUtils. Detailed failure reason(s): Could not determine the field where the value produced by the call to sun.misc.Unsafe.objectFieldOffset(Field) for the field offset computation is stored. The call is not directly followed by a field store or by a sign extend node followed directly by a field store.
Warning: RecomputeFieldValue.FieldOffset automatic substitution failed. The automatic substitution registration was attempted because a call to sun.misc.Unsafe.objectFieldOffset(Field) was detected in the static initializer of com.alibaba.fastjson2.util.JDKUtils. Detailed failure reason(s): Could not determine the field where the value produced by the call to sun.misc.Unsafe.objectFieldOffset(Field) for the field offset computation is stored. The call is not directly followed by a field store or by a sign extend node followed directly by a field store.
Warning: RecomputeFieldValue.FieldOffset automatic substitution failed. The automatic substitution registration was attempted because a call to sun.misc.Unsafe.objectFieldOffset(Field) was detected in the static initializer of com.alibaba.fastjson2.util.JDKUtils. Detailed failure reason(s): Could not determine the field where the value produced by the call to sun.misc.Unsafe.objectFieldOffset(Field) for the field offset computation is stored. The call is not directly followed by a field store or by a sign extend node followed directly by a field store.
Warning: RecomputeFieldValue.FieldOffset automatic substitution failed. The automatic substitution registration was attempted because a call to sun.misc.Unsafe.objectFieldOffset(Field) was detected in the static initializer of com.alibaba.fastjson2.util.JDKUtils. Detailed failure reason(s): The argument of sun.misc.Unsafe.objectFieldOffset(java.lang.reflect.Field) is not a constant value or a field load that can be constant-folded., Could not determine the field where the value produced by the call to sun.misc.Unsafe.objectFieldOffset(Field) for the field offset computation is stored. The call is not directly followed by a field store or by a sign extend node followed directly by a field store.
Warning: RecomputeFieldValue.FieldOffset automatic substitution failed. The automatic substitution registration was attempted because a call to sun.misc.Unsafe.objectFieldOffset(Field) was detected in the static initializer of com.alibaba.fastjson2.util.JDKUtils. Detailed failure reason(s): Could not determine the field where the value produced by the call to sun.misc.Unsafe.objectFieldOffset(Field) for the field offset computation is stored. The call is not directly followed by a field store or by a sign extend node followed directly by a field store.
According to https://developers.redhat.com/articles/2022/05/09/using-unsafe-safely-graalvm-native-image#unsafe_proves_to_be_unsafe, the static field in com.alibaba.fastjson2.util.JDKUtils which is assigned from UnSafe offset must be marked as final. It works after modification as below:
static {
Unsafe unsafe;
long offset, charOffset;
try {
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
unsafe = (Unsafe) theUnsafeField.get(null);
ARRAY_BYTE_BASE_OFFSET = unsafe.arrayBaseOffset(byte[].class);
ARRAY_CHAR_BASE_OFFSET = unsafe.arrayBaseOffset(char[].class);
} catch (Throwable e) {
throw new JSONException("init unsafe error", e);
}
UNSAFE = unsafe;
// ARRAY_BYTE_BASE_OFFSET = offset;
// ARRAY_CHAR_BASE_OFFSET = charOffset;
if (ARRAY_BYTE_BASE_OFFSET == -1) {
throw new JSONException("init JDKUtils error", initErrorLast);
}
问题描述
If a bean contains a boolean field, it can't be deserialized with false value in GraalVM native image.
环境信息
重现步骤
I'd created a demo repo in: https://github.com/suragreat/fastjson2-native.
To run:
mvn clean package -Pnative
./target/my-app
期待的正确结果
Deserialized from json string successfully.
相关日志输出
original json: {"height":800,"size":"LARGE","title":"image","transparent":"false","uri":"file://image.png","width":600}
image from original json: io.suragreat.issue.fastjson2.graalvm_native.vo.Image@3c49d008
toJSONString: {"height":800,"size":"LARGE","title":"image","transparent":false,"uri":"file://image.png","width":600}
Exception in thread "main" com.alibaba.fastjson2.JSONException: syntax error : 102
at com.alibaba.fastjson2.JSONReaderUTF8.readBoolValue(JSONReaderUTF8.java:7550)
at com.alibaba.fastjson2.reader.FieldReaderBoolValueMethod.readFieldValue(FieldReaderBoolValueMethod.java:26)
at com.alibaba.fastjson2.reader.ObjectReader6.readObject(ObjectReader6.java:389)
at com.alibaba.fastjson2.JSON.parseObject(JSON.java:864)
at io.suragreat.issue.fastjson2.graalvm_native.App.main(App.java:18)
Root Cause
The root cause is incorrectly Using Unsafe safely in GraalVM Native Image. From the native image building info, there're some warning listed as below:
According to https://developers.redhat.com/articles/2022/05/09/using-unsafe-safely-graalvm-native-image#unsafe_proves_to_be_unsafe, the
staticfield incom.alibaba.fastjson2.util.JDKUtilswhich is assigned from UnSafe offset must be marked asfinal. It works after modification as below: