Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Failed to deserialize boolean field with false value in GraalVM native image. #3108

Open
suragreat opened this issue Oct 17, 2024 · 0 comments
Labels
bug Something isn't working

Comments

@suragreat
Copy link

问题描述

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);
        }
@suragreat suragreat added the bug Something isn't working label Oct 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant