All Downloads are FREE. Search and download functionalities are using the official Maven repository.

shade.com.alibaba.fastjson2.util.JDKUtils Maven / Gradle / Ivy

There is a newer version: 1.3.7
Show newest version
package com.alibaba.fastjson2.util;

import com.alibaba.fastjson2.JSONException;
import sun.misc.Unsafe;

import java.lang.invoke.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteOrder;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.*;

import static java.lang.invoke.MethodType.methodType;

public class JDKUtils {
    public static final Unsafe UNSAFE;
    public static final long ARRAY_BYTE_BASE_OFFSET;
    public static final long ARRAY_CHAR_BASE_OFFSET;

    public static final int JVM_VERSION;
    public static final Byte LATIN1 = 0;
    public static final Byte UTF16 = 1;

    public static final Field FIELD_STRING_VALUE;
    public static final long FIELD_STRING_VALUE_OFFSET;
    public static volatile boolean FIELD_STRING_VALUE_ERROR;

    public static final long FIELD_DECIMAL_INT_COMPACT_OFFSET;
    public static final long FIELD_BIGINTEGER_MAG_OFFSET;

    public static final Field FIELD_STRING_CODER;
    public static final long FIELD_STRING_CODER_OFFSET;
    public static volatile boolean FIELD_STRING_CODER_ERROR;

    static final Class CLASS_SQL_DATASOURCE;
    static final Class CLASS_SQL_ROW_SET;
    public static final boolean HAS_SQL;
    public static final boolean ANDROID;
    public static final boolean GRAAL;
    public static final boolean OPENJ9;
    public static final int ANDROID_SDK_INT;

    // Android not support
    public static final Class CLASS_TRANSIENT;
    public static final boolean BIG_ENDIAN;

    public static final boolean UNSAFE_SUPPORT = true;
    public static final boolean VECTOR_SUPPORT;
    public static final int VECTOR_BIT_LENGTH;

    // GraalVM not support
    // Android not support
    public static final BiFunction STRING_CREATOR_JDK8;
    public static final BiFunction STRING_CREATOR_JDK11;
    public static final ToIntFunction STRING_CODER;
    public static final Function STRING_VALUE;

    public static final MethodHandle METHOD_HANDLE_HAS_NEGATIVE;
    public static final Predicate PREDICATE_IS_ASCII;

    static final MethodHandles.Lookup IMPL_LOOKUP;
    static volatile MethodHandle CONSTRUCTOR_LOOKUP;
    static volatile boolean CONSTRUCTOR_LOOKUP_ERROR;
    static volatile Throwable initErrorLast;
    static volatile Throwable reflectErrorLast;
    static final AtomicInteger reflectErrorCount = new AtomicInteger();

    static {
        Unsafe unsafe = null;
        long offset = -1, charOffset = -1;
        try {
            Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafeField.setAccessible(true);
            unsafe = (Unsafe) theUnsafeField.get(null);
            offset = unsafe.arrayBaseOffset(byte[].class);
            charOffset = unsafe.arrayBaseOffset(char[].class);
        } catch (Throwable e) {
            initErrorLast = e;
        }

        UNSAFE = unsafe;
        ARRAY_BYTE_BASE_OFFSET = offset;
        ARRAY_CHAR_BASE_OFFSET = charOffset;

        if (offset == -1) {
            throw new JSONException("init JDKUtils error", initErrorLast);
        }

        int jvmVersion = -1, android_sdk_int = -1;
        boolean openj9 = false, android = false, graal = false;
        try {
            String jmvName = System.getProperty("java.vm.name");
            openj9 = jmvName.contains("OpenJ9");
            android = "Dalvik".equals(jmvName);
            graal = System.getProperty("org.graalvm.nativeimage.imagecode") != null;
            if (openj9 || android || graal) {
                FIELD_STRING_VALUE_ERROR = true;
            }

            String javaSpecVer = System.getProperty("java.specification.version");
            // android is 0.9
            if (javaSpecVer.startsWith("1.")) {
                javaSpecVer = javaSpecVer.substring(2);
            }
            if (javaSpecVer.indexOf('.') == -1) {
                jvmVersion = Integer.parseInt(javaSpecVer);
            }

            if (android) {
                android_sdk_int = Class.forName("android.os.Build$VERSION")
                        .getField("SDK_INT")
                        .getInt(null);
            }
        } catch (Throwable e) {
            initErrorLast = e;
        }

        OPENJ9 = openj9;
        ANDROID = android;
        GRAAL = graal;
        ANDROID_SDK_INT = android_sdk_int;

        boolean hasJavaSql = true;
        Class dataSourceClass = null;
        Class rowSetClass = null;
        try {
            dataSourceClass = Class.forName("javax.sql.DataSource");
            rowSetClass = Class.forName("javax.sql.RowSet");
        } catch (Throwable ignored) {
            hasJavaSql = false;
        }
        CLASS_SQL_DATASOURCE = dataSourceClass;
        CLASS_SQL_ROW_SET = rowSetClass;
        HAS_SQL = hasJavaSql;

        Class transientClass = null;
        if (!android) {
            try {
                transientClass = Class.forName("java.beans.Transient");
            } catch (Throwable ignored) {
            }
        }
        CLASS_TRANSIENT = transientClass;

        JVM_VERSION = jvmVersion;

        if (JVM_VERSION == 8) {
            Field field = null;
            long fieldOffset = -1;
            if (!ANDROID) {
                try {
                    field = String.class.getDeclaredField("value");
                    field.setAccessible(true);
                    fieldOffset = UNSAFE.objectFieldOffset(field);
                } catch (Exception ignored) {
                    FIELD_STRING_VALUE_ERROR = true;
                }
            }

            FIELD_STRING_VALUE = field;
            FIELD_STRING_VALUE_OFFSET = fieldOffset;

            FIELD_STRING_CODER = null;
            FIELD_STRING_CODER_OFFSET = -1;
            FIELD_STRING_CODER_ERROR = true;
        } else {
            Field fieldValue = null;
            long fieldValueOffset = -1;
            if (!ANDROID) {
                try {
                    fieldValue = String.class.getDeclaredField("value");
                    fieldValueOffset = UNSAFE.objectFieldOffset(fieldValue);
                } catch (Exception ignored) {
                    FIELD_STRING_VALUE_ERROR = true;
                }
            }
            FIELD_STRING_VALUE_OFFSET = fieldValueOffset;
            FIELD_STRING_VALUE = fieldValue;

            Field fieldCode = null;
            long fieldCodeOffset = -1;
            if (!ANDROID) {
                try {
                    fieldCode = String.class.getDeclaredField("coder");
                    fieldCodeOffset = UNSAFE.objectFieldOffset(fieldCode);
                } catch (Exception ignored) {
                    FIELD_STRING_CODER_ERROR = true;
                }
            }
            FIELD_STRING_CODER_OFFSET = fieldCodeOffset;
            FIELD_STRING_CODER = fieldCode;
        }

        {
            long fieldOffset = -1;
            try {
                Field field = BigDecimal.class.getDeclaredField("intCompact");
                fieldOffset = UNSAFE.objectFieldOffset(field);
            } catch (Throwable ignored) {
                // ignored
            }
            FIELD_DECIMAL_INT_COMPACT_OFFSET = fieldOffset;
        }

        {
            long fieldOffset = -1;
            try {
                Field field = BigInteger.class.getDeclaredField("mag");
                fieldOffset = UNSAFE.objectFieldOffset(field);
            } catch (Throwable ignored) {
                // ignored
            }
            FIELD_BIGINTEGER_MAG_OFFSET = fieldOffset;
        }

        BIG_ENDIAN = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN;

        BiFunction stringCreatorJDK8 = null;
        BiFunction stringCreatorJDK11 = null;
        ToIntFunction stringCoder = null;
        Function stringValue = null;

        MethodHandles.Lookup trustedLookup = null;
        if (!ANDROID) {
            try {
                Class lookupClass = MethodHandles.Lookup.class;
                Field implLookup = lookupClass.getDeclaredField("IMPL_LOOKUP");
                long fieldOffset = UNSAFE.staticFieldOffset(implLookup);
                trustedLookup = (MethodHandles.Lookup) UNSAFE.getObject(lookupClass, fieldOffset);
            } catch (Throwable ignored) {
                // ignored
            }
            if (trustedLookup == null) {
                trustedLookup = MethodHandles.lookup();
            }
        }
        IMPL_LOOKUP = trustedLookup;

        int vector_bit_length = -1;
        boolean vector_support = false;
        try {
            if (JVM_VERSION >= 11) {
                Class factorClass = Class.forName("java.lang.management.ManagementFactory");
                Class runtimeMXBeanClass = Class.forName("java.lang.management.RuntimeMXBean");
                Method getRuntimeMXBean = factorClass.getMethod("getRuntimeMXBean");
                Object runtimeMXBean = getRuntimeMXBean.invoke(null);
                Method getInputArguments = runtimeMXBeanClass.getMethod("getInputArguments");
                List inputArguments = (List) getInputArguments.invoke(runtimeMXBean);
                vector_support = inputArguments.contains("--add-modules=jdk.incubator.vector");

                if (vector_support) {
                    Class byteVectorClass = Class.forName("jdk.incubator.vector.ByteVector");
                    Class vectorSpeciesClass = Class.forName("jdk.incubator.vector.VectorSpecies");
                    Field speciesMax = byteVectorClass.getField("SPECIES_MAX");
                    Object species = speciesMax.get(null);
                    Method lengthMethod = vectorSpeciesClass.getMethod("length");
                    int length = (Integer) lengthMethod.invoke(species);
                    vector_bit_length = length * 8;
                }
            }
        } catch (Throwable e) {
            initErrorLast = e;
        }
        VECTOR_SUPPORT = vector_support;
        VECTOR_BIT_LENGTH = vector_bit_length;

        {
            Predicate isAscii = null;
            // isASCII
            MethodHandle handle = null;
            Class classStringCoding = null;
            if (JVM_VERSION >= 17) {
                try {
                    handle = trustedLookup.findStatic(
                            classStringCoding = String.class,
                            "isASCII",
                            MethodType.methodType(boolean.class, byte[].class)
                    );
                } catch (Throwable e) {
                    initErrorLast = e;
                }
            }

            if (handle == null && JVM_VERSION >= 11) {
                try {
                    classStringCoding = Class.forName("java.lang.StringCoding");
                    handle = trustedLookup.findStatic(
                            classStringCoding,
                            "isASCII",
                            MethodType.methodType(boolean.class, byte[].class)
                    );
                } catch (Throwable e) {
                    initErrorLast = e;
                }
            }

            if (handle != null) {
                try {
                    MethodHandles.Lookup lookup = trustedLookup(classStringCoding);
                    CallSite callSite = LambdaMetafactory.metafactory(
                            lookup,
                            "test",
                            methodType(Predicate.class),
                            methodType(boolean.class, Object.class),
                            handle,
                            methodType(boolean.class, byte[].class)
                    );
                    isAscii = (Predicate) callSite.getTarget().invokeExact();
                } catch (Throwable e) {
                    initErrorLast = e;
                }
            }

            PREDICATE_IS_ASCII = isAscii;
        }

        {
            MethodHandle handle = null;
            if (JVM_VERSION >= 11) {
                try {
                    Class classStringCoding = Class.forName("java.lang.StringCoding");
                    handle = trustedLookup.findStatic(
                            classStringCoding,
                            "hasNegatives",
                            MethodType.methodType(boolean.class, byte[].class, int.class, int.class)
                    );
                } catch (Throwable e) {
                    initErrorLast = e;
                }
            }
            METHOD_HANDLE_HAS_NEGATIVE = handle;
        }

        Boolean compact_strings = null;
        try {
            if (JVM_VERSION == 8) {
                MethodHandles.Lookup lookup = trustedLookup(String.class);

                MethodHandle handle = lookup.findConstructor(
                        String.class, methodType(void.class, char[].class, boolean.class)
                );

                CallSite callSite = LambdaMetafactory.metafactory(
                        lookup,
                        "apply",
                        methodType(BiFunction.class),
                        methodType(Object.class, Object.class, Object.class),
                        handle,
                        methodType(String.class, char[].class, boolean.class)
                );
                stringCreatorJDK8 = (BiFunction) callSite.getTarget().invokeExact();
            }

            boolean lookupLambda = false;
            if (JVM_VERSION > 8 && !android) {
                try {
                    Field compact_strings_field = String.class.getDeclaredField("COMPACT_STRINGS");
                    long fieldOffset = UNSAFE.staticFieldOffset(compact_strings_field);
                    compact_strings = UNSAFE.getBoolean(String.class, fieldOffset);
                } catch (Throwable e) {
                    initErrorLast = e;
                }
                lookupLambda = compact_strings != null && compact_strings;
            }

            if (lookupLambda) {
                MethodHandles.Lookup lookup = trustedLookup.in(String.class);
                MethodHandle handle = lookup.findConstructor(
                        String.class, methodType(void.class, byte[].class, byte.class)
                );
                CallSite callSite = LambdaMetafactory.metafactory(
                        lookup,
                        "apply",
                        methodType(BiFunction.class),
                        methodType(Object.class, Object.class, Object.class),
                        handle,
                        methodType(String.class, byte[].class, Byte.class)
                );
                stringCreatorJDK11 = (BiFunction) callSite.getTarget().invokeExact();

                MethodHandle coder = lookup.findSpecial(
                        String.class,
                        "coder",
                        methodType(byte.class),
                        String.class
                );
                CallSite applyAsInt = LambdaMetafactory.metafactory(
                        lookup,
                        "applyAsInt",
                        methodType(ToIntFunction.class),
                        methodType(int.class, Object.class),
                        coder,
                        methodType(byte.class, String.class)
                );
                stringCoder = (ToIntFunction) applyAsInt.getTarget().invokeExact();

                MethodHandle value = lookup.findSpecial(
                        String.class,
                        "value",
                        methodType(byte[].class),
                        String.class
                );
                CallSite apply = LambdaMetafactory.metafactory(
                        lookup,
                        "apply",
                        methodType(Function.class),
                        methodType(Object.class, Object.class),
                        value,
                        methodType(byte[].class, String.class)
                );
                stringValue = (Function) apply.getTarget().invokeExact();
            }
        } catch (Throwable e) {
            initErrorLast = e;
        }

        if (stringCoder == null) {
            stringCoder = (str) -> 1;
        }

        STRING_CREATOR_JDK8 = stringCreatorJDK8;
        STRING_CREATOR_JDK11 = stringCreatorJDK11;
        STRING_CODER = stringCoder;
        STRING_VALUE = stringValue;
    }

    public static boolean isSQLDataSourceOrRowSet(Class type) {
        return (CLASS_SQL_DATASOURCE != null && CLASS_SQL_DATASOURCE.isAssignableFrom(type))
                || (CLASS_SQL_ROW_SET != null && CLASS_SQL_ROW_SET.isAssignableFrom(type));
    }

    public static void setReflectErrorLast(Throwable error) {
        reflectErrorCount.incrementAndGet();
        reflectErrorLast = error;
    }

    public static char[] getCharArray(String str) {
        // GraalVM not support
        // Android not support
        if (!FIELD_STRING_VALUE_ERROR) {
            try {
                return (char[]) UNSAFE.getObject(str, FIELD_STRING_VALUE_OFFSET);
            } catch (Exception ignored) {
                FIELD_STRING_VALUE_ERROR = true;
            }
        }

        return str.toCharArray();
    }

    public static MethodHandles.Lookup trustedLookup(Class objectClass) {
        if (!CONSTRUCTOR_LOOKUP_ERROR) {
            try {
                int TRUSTED = -1;

                MethodHandle constructor = CONSTRUCTOR_LOOKUP;
                if (JVM_VERSION < 15) {
                    if (constructor == null) {
                        constructor = IMPL_LOOKUP.findConstructor(
                                MethodHandles.Lookup.class,
                                methodType(void.class, Class.class, int.class)
                        );
                        CONSTRUCTOR_LOOKUP = constructor;
                    }
                    int FULL_ACCESS_MASK = 31; // for IBM Open J9 JDK
                    return (MethodHandles.Lookup) constructor.invoke(
                            objectClass,
                            OPENJ9 ? FULL_ACCESS_MASK : TRUSTED
                    );
                } else {
                    if (constructor == null) {
                        constructor = IMPL_LOOKUP.findConstructor(
                                MethodHandles.Lookup.class,
                                methodType(void.class, Class.class, Class.class, int.class)
                        );
                        CONSTRUCTOR_LOOKUP = constructor;
                    }
                    return (MethodHandles.Lookup) constructor.invoke(objectClass, null, TRUSTED);
                }
            } catch (Throwable ignored) {
                CONSTRUCTOR_LOOKUP_ERROR = true;
            }
        }

        return IMPL_LOOKUP.in(objectClass);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy