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

[GR-58000] Native Image support for GetStringUTFLengthAsLong in JNI_VERSION_24 (JDK-8328877) #9751

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion sdk/src/org.graalvm.jniutils/src/org/graalvm/jniutils/JNI.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
Expand Down Expand Up @@ -219,6 +219,9 @@ public interface JNINativeInterface extends PointerBase {
@CField("GetStringUTFLength")
GetStringUTFLength getGetStringUTFLength();

@CField("GetStringUTFLengthAsLong")
GetStringUTFLengthAsLong getGetStringUTFLengthAsLong();

@CField("GetStringUTFChars")
GetStringUTFChars getGetStringUTFChars();

Expand Down Expand Up @@ -783,6 +786,11 @@ public interface GetStringUTFLength extends CFunctionPointer {
int call(JNIEnv env, JString str);
}

public interface GetStringUTFLengthAsLong extends CFunctionPointer {
@InvokeCFunctionPointer
long call(JNIEnv env, JString str);
}

public interface IsSameObject extends CFunctionPointer {
@InvokeCFunctionPointer
boolean call(JNIEnv env, JObject ref1, JObject ref2);
Expand Down
1 change: 1 addition & 0 deletions substratevm/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ At runtime, premain runtime options are set along with main class' arguments in
* (GR-48384) Added a GDB Python script (`gdb-debughelpers.py`) to improve the Native Image debugging experience.
* (GR-49517) Add support for emitting Windows x64 unwind info. This enables stack walking in native tooling such as debuggers and profilers.
* (GR-57384) Preserve the origin of a resource included in a native image. The information is included in the report produced by -H:+GenerateEmbeddedResourcesFile.
* (GR-58000) Support for `GetStringUTFLengthAsLong` added in JNI_VERSION_24 ([JDK-8328877](https://bugs.openjdk.org/browse/JDK-8328877))

## GraalVM for JDK 23 (Internal Version 24.1.0)
* (GR-51520) The old class initialization strategy, which was deprecated in GraalVM for JDK 22, is removed. The option `StrictImageHeap` no longer has any effect.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,22 @@ public final class Utf8 {
private Utf8() {
}

private static int utf8Size(char c) {
// Based On
// https://github.com/openjdk/jdk/blob/jdk-24+16/src/hotspot/share/utilities/utf8.cpp#L479-L488
if ((0x0001 <= c) && (c <= 0x007F)) {
// ASCII character
return 1;
} else if (c <= 0x07FF) {
return 2;
} else {
return 3;
}
}

/**
* @return the length in bytes of the UTF8 representation of the string
* @return the length as {@code int} in bytes of the UTF8 representation of the string. Might
* return a truncated size if the value does not fit into {@code int} (see JDK-8328877).
*/
public static int utf8Length(String string) {
return utf8Length(string, 0, string.length());
Expand All @@ -46,24 +60,29 @@ public static int utf8Length(String string) {
/**
* @param beginIndex first index that is part of the region, inclusive
* @param endIndex index at the end of the region, exclusive
* @return the length in bytes of the UTF8 representation of the string region
* @return the length as {@code int} in bytes of the UTF8 representation of the string region.
* Might return a truncated size if the value does not fit into {@code int} (see
* JDK-8328877).
*/
public static int utf8Length(String s, int beginIndex, int endIndex) {
// Based on
// https://github.com/openjdk/jdk/blob/jdk-24+16/src/hotspot/share/utilities/utf8.cpp#L511-L526.
if (beginIndex < 0 || endIndex > s.length() || beginIndex > endIndex) {
throw new StringIndexOutOfBoundsException();
}
int length = 0;
for (int i = beginIndex; i < endIndex; i++) {
final int c = s.charAt(i);
if ((c >= 0x0001) && (c <= 0x007F)) {
length++;
} else if (c > 0x07FF) {
length += 3;
} else {
length += 2;
long result = 0;
for (int index = beginIndex; index < endIndex; index++) {
char c = s.charAt(index);
long sz = utf8Size(c);
// If the length is > INT_MAX-1 we truncate at a completed
// modified-UTF8 encoding. This allows for +1 to be added
// by the caller for NUL-termination, without overflow.
if (result + sz > Integer.MAX_VALUE - 1) {
break;
}
result += sz;
}
return length;
return (int) result;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
*/
package com.oracle.svm.core.jni.functions;

import jdk.graal.compiler.word.Word;
import org.graalvm.nativeimage.CurrentIsolate;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
Expand All @@ -45,8 +44,11 @@
import com.oracle.svm.core.jni.headers.JNIInvokeInterface;
import com.oracle.svm.core.jni.headers.JNIJavaVM;
import com.oracle.svm.core.jni.headers.JNINativeInterface;
import com.oracle.svm.core.jni.headers.JNINativeInterfaceJDKLatest;
import com.oracle.svm.core.util.VMError;

import jdk.graal.compiler.serviceprovider.JavaVersionUtil;
import jdk.graal.compiler.word.Word;
import jdk.internal.misc.Unsafe;

/**
Expand All @@ -73,7 +75,7 @@ public static JNIFunctionTables singleton() {
private final CIsolateData<JNIJavaVM> jniJavaVM = CIsolateDataFactory.createStruct("jniJavaVM", JNIJavaVM.class);

private static int getFunctionTableSize() {
return SizeOf.get(JNINativeInterface.class);
return JavaVersionUtil.JAVA_SPEC > 21 ? SizeOf.get(JNINativeInterfaceJDKLatest.class) : SizeOf.get(JNINativeInterface.class);
}

@Platforms(Platform.HOSTED_ONLY.class)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.core.jni.functions;

import org.graalvm.nativeimage.c.function.CEntryPoint;
import org.graalvm.nativeimage.c.function.CEntryPoint.Publish;

import com.oracle.svm.core.c.function.CEntryPointOptions;
import com.oracle.svm.core.jni.JNIObjectHandles;
import com.oracle.svm.core.jni.functions.JNIFunctions.Support.JNIEnvEnterPrologue;
import com.oracle.svm.core.jni.functions.JNIFunctions.Support.JNIExceptionHandlerReturnMinusOne;
import com.oracle.svm.core.jni.functions.JNIFunctions.Support.ReturnMinusOneLong;
import com.oracle.svm.core.jni.headers.JNIEnvironment;
import com.oracle.svm.core.jni.headers.JNIObjectHandle;
import com.oracle.svm.core.util.Utf8;

/**
* Implementations of the functions defined by the Java Native Interface only present in the latest
* supported JDK.
*
* @see JNIFunctions
*/
@SuppressWarnings("unused")
public final class JNIFunctionsJDKLatest {

// Checkstyle: stop

/*
* jlong GetStringUTFLengthAsLong(JNIEnv *env, jstring string);
*/

@CEntryPoint(exceptionHandler = JNIExceptionHandlerReturnMinusOne.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished)
@CEntryPointOptions(prologue = JNIEnvEnterPrologue.class, prologueBailout = ReturnMinusOneLong.class)
static long GetStringUTFLengthAsLong(JNIEnvironment env, JNIObjectHandle hstr) {
String str = JNIObjectHandles.getObject(hstr);
return Utf8.utf8LengthAsLong(str);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.core.jni.headers;

import org.graalvm.nativeimage.c.CContext;
import org.graalvm.nativeimage.c.function.CFunctionPointer;
import org.graalvm.nativeimage.c.struct.CField;
import org.graalvm.nativeimage.c.struct.CStruct;

@CContext(JNIHeaderDirectivesJDKLatest.class)
@CStruct(value = "JNINativeInterface_", addStructKeyword = true)
public interface JNINativeInterfaceJDKLatest extends JNINativeInterface {

@CField
CFunctionPointer getGetStringUTFLengthAsLong();

@CField
void setGetStringUTFLengthAsLong(CFunctionPointer p);
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
public final class JNIVersion {
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static boolean isSupported(int version, boolean builtInLibrary) {
if (JavaVersionUtil.JAVA_SPEC >= 22 && version == JNIVersionJDKLatest.JNI_VERSION_LATEST()) {
if (JavaVersionUtil.JAVA_SPEC > 21 && version == JNIVersionJDKLatest.JNI_VERSION_LATEST()) {
return true;
}
if (version == JNI_VERSION_21() || version == JNI_VERSION_20() || version == JNI_VERSION_19() || version == JNI_VERSION_10() || version == JNI_VERSION_9() || version == JNI_VERSION_1_8()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,15 @@ public final class JNIVersionJDKLatest {

// Checkstyle: stop

@CConstant
public static native int JNI_VERSION_24();

/*
* GR-50948: there is not yet a JNI_VERSION_XX constant defined for JDK latest. As soon as it
* gets available, the "value" property of the CConstant annotation below must be removed.
*/
@CConstant(value = "JNI_VERSION_21")
@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+14/src/java.base/share/native/include/jni.h#L1985-L1996")
@CConstant(value = "JNI_VERSION_24")
@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+16/src/java.base/share/native/include/jni.h#L1994-L2006")
public static native int JNI_VERSION_LATEST();

// Checkstyle: resume
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,46 @@ public final class Utf8 {
private Utf8() {
}

@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+16/src/hotspot/share/utilities/utf8.cpp#L479-L488")
private static int utf8Size(char c) {
if ((0x0001 <= c) && (c <= 0x007F)) {
// ASCII character
return 1;
} else if (c <= 0x07FF) {
return 2;
} else {
return 3;
}
}

/**
* @return the length as {@code long} in bytes of the UTF8 representation of the string
*/
public static long utf8LengthAsLong(String string) {
return utf8LengthAsLong(string, 0, string.length());
}

/**
* @return the length in bytes of the UTF8 representation of the string
* @param beginIndex first index that is part of the region, inclusive
* @param endIndex index at the end of the region, exclusive
* @return the length as {@code long} in bytes of the UTF8 representation of the string
*/
@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+16/src/hotspot/share/utilities/utf8.cpp#L502-L509")
public static long utf8LengthAsLong(String s, int beginIndex, int endIndex) {
if (beginIndex < 0 || endIndex > s.length() || beginIndex > endIndex) {
throw new StringIndexOutOfBoundsException();
}
int length = s.length();
long result = 0;
for (int index = 0; index < length; index++) {
result += utf8Size(s.charAt(index));
}
return result;
}

/**
* @return the length as {@code int} in bytes of the UTF8 representation of the string. Might
* return a truncated size if the value does not fit into {@code int} (see JDK-8328877).
*/
public static int utf8Length(String string) {
return utf8Length(string, 0, string.length());
Expand All @@ -49,24 +87,27 @@ public static int utf8Length(String string) {
/**
* @param beginIndex first index that is part of the region, inclusive
* @param endIndex index at the end of the region, exclusive
* @return the length in bytes of the UTF8 representation of the string region
* @return the length as {@code int} in bytes of the UTF8 representation of the string. Might
* return a truncated size if the value does not fit into {@code int} (see JDK-8328877).
*/
@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+16/src/hotspot/share/utilities/utf8.cpp#L511-L526")
public static int utf8Length(String s, int beginIndex, int endIndex) {
if (beginIndex < 0 || endIndex > s.length() || beginIndex > endIndex) {
throw new StringIndexOutOfBoundsException();
}
int length = 0;
for (int i = beginIndex; i < endIndex; i++) {
final int c = s.charAt(i);
if ((c >= 0x0001) && (c <= 0x007F)) {
length++;
} else if (c > 0x07FF) {
length += 3;
} else {
length += 2;
long result = 0;
for (int index = beginIndex; index < endIndex; index++) {
char c = s.charAt(index);
long sz = utf8Size(c);
// If the length is > INT_MAX-1 we truncate at a completed
// modified-UTF8 encoding. This allows for +1 to be added
// by the caller for NUL-termination, without overflow.
if (result + sz > Integer.MAX_VALUE - 1) {
break;
}
result += sz;
}
return length;
return (int) result;
}

/**
Expand Down
Loading