diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsUtils.java b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsUtils.java index 9c823502a4cc..17be4b5e651b 100644 --- a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsUtils.java +++ b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsUtils.java @@ -27,9 +27,7 @@ import static com.oracle.svm.core.annotate.RecomputeFieldValue.Kind.Custom; import java.io.FileDescriptor; -import java.io.IOException; -import com.oracle.svm.core.util.BasedOnJDKFile; import org.graalvm.nativeimage.StackValue; import org.graalvm.nativeimage.c.function.CFunctionPointer; import org.graalvm.nativeimage.c.struct.CPointerTo; @@ -48,7 +46,7 @@ import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.c.function.CEntryPointActions; import com.oracle.svm.core.graal.stackvalue.UnsafeStackValue; -import com.oracle.svm.core.handles.PrimitiveArrayView; +import com.oracle.svm.core.util.BasedOnJDKFile; import com.oracle.svm.core.windows.headers.FileAPI; import com.oracle.svm.core.windows.headers.LibLoaderAPI; import com.oracle.svm.core.windows.headers.WinBase; @@ -84,10 +82,6 @@ static void setHandle(FileDescriptor descriptor, long handle) { SubstrateUtil.cast(descriptor, Target_java_io_FileDescriptor.class).handle = handle; } - static boolean outOfBounds(int off, int len, byte[] array) { - return off < 0 || len < 0 || array.length - off < len; - } - /** Return the error string for the last error, or a default message. */ public static String lastErrorString(String defaultMsg) { int error = WinBase.GetLastError(); @@ -133,38 +127,6 @@ static boolean flush(int handle) { return (result != 0); } - @SuppressWarnings("unused") - static void writeBytes(FileDescriptor descriptor, byte[] bytes, int off, int len, boolean append) throws IOException { - if (bytes == null) { - throw new NullPointerException(); - } else if (WindowsUtils.outOfBounds(off, len, bytes)) { - throw new IndexOutOfBoundsException(); - } - if (len == 0) { - return; - } - - try (PrimitiveArrayView bytesPin = PrimitiveArrayView.createForReading(bytes)) { - CCharPointer curBuf = bytesPin.addressOfArrayElement(off); - UnsignedWord curLen = WordFactory.unsigned(len); - /** Temp fix until we complete FileDescriptor substitutions. */ - int handle = FileAPI.GetStdHandle(FileAPI.STD_ERROR_HANDLE()); - - CIntPointer bytesWritten = UnsafeStackValue.get(CIntPointer.class); - - int ret = FileAPI.WriteFile(handle, curBuf, curLen, bytesWritten, WordFactory.nullPointer()); - - if (ret == 0) { - throw new IOException(lastErrorString("Write error")); - } - - int writtenCount = bytesWritten.read(); - if (curLen.notEqual(writtenCount)) { - throw new IOException(lastErrorString("Write error")); - } - } - } - private static long performanceFrequency = 0L; public static final long NANOSECS_PER_SEC = 1000000000L; public static final int NANOSECS_PER_MILLISEC = 1000000; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/JavaMemoryUtil.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/JavaMemoryUtil.java index 544206ce9509..af9c31b6b7e4 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/JavaMemoryUtil.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/JavaMemoryUtil.java @@ -24,8 +24,6 @@ */ package com.oracle.svm.core; -import jdk.graal.compiler.word.BarrieredAccess; -import jdk.graal.compiler.word.Word; import org.graalvm.word.Pointer; import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; @@ -34,6 +32,9 @@ import com.oracle.svm.core.hub.LayoutEncoding; import com.oracle.svm.core.util.VMError; +import jdk.graal.compiler.word.BarrieredAccess; +import jdk.graal.compiler.word.Word; + /** * The methods in this class are mainly used to fill or copy Java heap memory. All methods guarantee * at least some level of atomicity and honor the Java memory model if they are used as documented. @@ -268,7 +269,7 @@ public static void copyBackward(Pointer from, Pointer to, UnsignedWord size) { } @Uninterruptible(reason = "Memory is on the heap, copying must not be interrupted.") - static void copyOnHeap(Object srcBase, UnsignedWord srcOffset, Object destBase, UnsignedWord destOffset, UnsignedWord size) { + public static void copyOnHeap(Object srcBase, UnsignedWord srcOffset, Object destBase, UnsignedWord destOffset, UnsignedWord size) { Word fromPtr = Word.objectToUntrackedPointer(srcBase).add(srcOffset); Word toPtr = Word.objectToUntrackedPointer(destBase).add(destOffset); UnmanagedMemoryUtil.copy(fromPtr, toPtr, size); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateDiagnostics.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateDiagnostics.java index b868e3fc9c00..822e8fa90410 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateDiagnostics.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateDiagnostics.java @@ -24,6 +24,7 @@ */ package com.oracle.svm.core; +import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE; import static com.oracle.svm.core.option.RuntimeOptionKey.RuntimeOptionKeyFlag.RelevantForCompilationIsolates; import java.util.ArrayList; @@ -1307,11 +1308,24 @@ protected void onValueUpdate(EconomicMap, Object> values, Boolean o } }; + @Option(help = "Determines if implicit exceptions are fatal if they don't have a stack trace.", type = OptionType.Debug)// + public static final RuntimeOptionKey ImplicitExceptionWithoutStacktraceIsFatal = new RuntimeOptionKey<>(false) { + @Override + protected void onValueUpdate(EconomicMap, Object> values, Boolean oldValue, Boolean newValue) { + super.onValueUpdate(values, oldValue, newValue); + if (ImageSingletons.contains(Options.class)) { + Options.singleton().implicitExceptionWithoutStacktraceIsFatal = newValue; + } + } + }; + private volatile boolean loopOnFatalError; + private boolean implicitExceptionWithoutStacktraceIsFatal; @Platforms(Platform.HOSTED_ONLY.class) public Options() { this.loopOnFatalError = Options.LoopOnFatalError.getValue(); + this.implicitExceptionWithoutStacktraceIsFatal = Options.ImplicitExceptionWithoutStacktraceIsFatal.getValue(); } @Fold @@ -1322,5 +1336,10 @@ static Options singleton() { public static boolean shouldLoopOnFatalError() { return singleton().loopOnFatalError; } + + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static boolean implicitExceptionWithoutStacktraceIsFatal() { + return singleton().implicitExceptionWithoutStacktraceIsFatal; + } } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index 79c1351e1cc7..6759646a916a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -1091,9 +1091,6 @@ protected void onValueUpdate(EconomicMap, Object> values, String ol @Option(help = "Specifies the number of entries that diagnostic buffers have.", type = OptionType.Debug)// public static final HostedOptionKey DiagnosticBufferSize = new HostedOptionKey<>(30); - @Option(help = "Determines if implicit exceptions are fatal if they don't have a stack trace.", type = OptionType.Debug)// - public static final RuntimeOptionKey ImplicitExceptionWithoutStacktraceIsFatal = new RuntimeOptionKey<>(false); - @Option(help = "Determines if frame anchors are verified at run-time.", type = OptionType.Debug)// public static final HostedOptionKey VerifyFrameAnchors = new HostedOptionKey<>(false); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoDecoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoDecoder.java index e40d8aabf569..4a6e99458239 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoDecoder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoDecoder.java @@ -335,7 +335,7 @@ private static long loadReferenceMapIndex(CodeInfo info, long entryOffset, int e static final int FRAME_SIZE_STATUS_MASK = FRAME_SIZE_METHOD_START | FRAME_SIZE_ENTRY_POINT | FRAME_SIZE_HAS_CALLEE_SAVED_REGISTERS; @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - static boolean decodeIsEntryPoint(long sizeEncoding) { + public static boolean decodeIsEntryPoint(long sizeEncoding) { assert sizeEncoding != INVALID_SIZE_ENCODING; return (sizeEncoding & FRAME_SIZE_ENTRY_POINT) != 0; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/jdk/SubstrateArraycopySnippets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/jdk/SubstrateArraycopySnippets.java index a590dab48a60..a387e647f5aa 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/jdk/SubstrateArraycopySnippets.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/jdk/SubstrateArraycopySnippets.java @@ -42,6 +42,7 @@ import com.oracle.svm.core.snippets.SnippetRuntime; import com.oracle.svm.core.snippets.SnippetRuntime.SubstrateForeignCallDescriptor; import com.oracle.svm.core.snippets.SubstrateForeignCallTarget; +import com.oracle.svm.core.util.ArrayUtil; import jdk.graal.compiler.graph.Node; import jdk.graal.compiler.graph.NodeClass; @@ -50,7 +51,6 @@ import jdk.graal.compiler.nodes.StructuredGraph; import jdk.graal.compiler.nodes.ValueNode; import jdk.graal.compiler.nodes.extended.ForeignCallWithExceptionNode; -import jdk.graal.compiler.nodes.java.ArrayLengthNode; import jdk.graal.compiler.nodes.spi.Lowerable; import jdk.graal.compiler.nodes.spi.LoweringTool; import jdk.graal.compiler.options.OptionValues; @@ -88,25 +88,25 @@ private static void doArraycopy(Object fromArray, int fromIndex, Object toArray, if (LayoutEncoding.isPrimitiveArray(fromLayoutEncoding)) { if (fromArray == toArray && fromIndex < toIndex) { - boundsCheck(fromArray, fromIndex, toArray, toIndex, length); + ArrayUtil.boundsCheckInSnippet(fromArray, fromIndex, toArray, toIndex, length); JavaMemoryUtil.copyPrimitiveArrayBackward(fromArray, fromIndex, fromArray, toIndex, length, fromLayoutEncoding); return; } else if (fromHub == toHub) { - boundsCheck(fromArray, fromIndex, toArray, toIndex, length); + ArrayUtil.boundsCheckInSnippet(fromArray, fromIndex, toArray, toIndex, length); JavaMemoryUtil.copyPrimitiveArrayForward(fromArray, fromIndex, toArray, toIndex, length, fromLayoutEncoding); return; } } else if (LayoutEncoding.isObjectArray(fromLayoutEncoding)) { if (fromArray == toArray && fromIndex < toIndex) { - boundsCheck(fromArray, fromIndex, toArray, toIndex, length); + ArrayUtil.boundsCheckInSnippet(fromArray, fromIndex, toArray, toIndex, length); JavaMemoryUtil.copyObjectArrayBackward(fromArray, fromIndex, fromArray, toIndex, length, fromLayoutEncoding); return; } else if (fromHub == toHub) { - boundsCheck(fromArray, fromIndex, toArray, toIndex, length); + ArrayUtil.boundsCheckInSnippet(fromArray, fromIndex, toArray, toIndex, length); JavaMemoryUtil.copyObjectArrayForward(fromArray, fromIndex, toArray, toIndex, length, fromLayoutEncoding); return; } else if (LayoutEncoding.isObjectArray(toHub.getLayoutEncoding())) { - boundsCheck(fromArray, fromIndex, toArray, toIndex, length); + ArrayUtil.boundsCheckInSnippet(fromArray, fromIndex, toArray, toIndex, length); if (DynamicHub.toClass(toHub).isAssignableFrom(DynamicHub.toClass(fromHub))) { JavaMemoryUtil.copyObjectArrayForward(fromArray, fromIndex, toArray, toIndex, length, fromLayoutEncoding); } else { @@ -118,12 +118,6 @@ private static void doArraycopy(Object fromArray, int fromIndex, Object toArray, throw new ArrayStoreException(); } - private static void boundsCheck(Object fromArray, int fromIndex, Object toArray, int toIndex, int length) { - if (fromIndex < 0 || toIndex < 0 || length < 0 || fromIndex > ArrayLengthNode.arrayLength(fromArray) - length || toIndex > ArrayLengthNode.arrayLength(toArray) - length) { - throw new ArrayIndexOutOfBoundsException(); - } - } - static final class SubstrateArrayCopyLowering implements NodeLoweringProvider { @Override public void lower(ArrayCopyNode node, LoweringTool tool) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/replacements/SubstrateGraphKit.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/replacements/SubstrateGraphKit.java index 53c2eed7d988..3e3a2d4da8a2 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/replacements/SubstrateGraphKit.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/replacements/SubstrateGraphKit.java @@ -70,7 +70,6 @@ import jdk.graal.compiler.nodes.extended.BoxNode; import jdk.graal.compiler.nodes.extended.FixedValueAnchorNode; import jdk.graal.compiler.nodes.extended.GuardingNode; -import jdk.graal.compiler.nodes.extended.StateSplitProxyNode; import jdk.graal.compiler.nodes.extended.UnboxNode; import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; import jdk.graal.compiler.nodes.java.ExceptionObjectNode; @@ -372,10 +371,4 @@ protected T appendWithUnwind(T withExceptionNode, return withExceptionNode; } - - public void appendStateSplitProxy() { - StateSplitProxyNode proxy = new StateSplitProxyNode(); - append(proxy); - proxy.setStateAfter(frameState.create(bci(), proxy)); - } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/handles/ThreadLocalHandles.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/handles/ThreadLocalHandles.java index 86b0cd8a5b75..5ae87ea2fb24 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/handles/ThreadLocalHandles.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/handles/ThreadLocalHandles.java @@ -129,6 +129,7 @@ public void popFrame() { popFramesIncluding(frameCount); } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public void popFramesIncluding(int frame) { assert frame > 0 && frame <= frameCount; int previousTop = top; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIGeneratedMethodSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIGeneratedMethodSupport.java index d1551e7ef54a..c0567416e479 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIGeneratedMethodSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIGeneratedMethodSupport.java @@ -24,26 +24,13 @@ */ package com.oracle.svm.core.jni; -import java.lang.reflect.Array; - -import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.word.PointerBase; -import org.graalvm.word.WordBase; -import org.graalvm.word.WordFactory; -import com.oracle.svm.core.StaticFieldsSupport; import com.oracle.svm.core.Uninterruptible; -import com.oracle.svm.core.config.ConfigurationValues; -import com.oracle.svm.core.handles.PrimitiveArrayView; -import com.oracle.svm.core.jni.access.JNIAccessibleField; import com.oracle.svm.core.jni.access.JNINativeLinkage; import com.oracle.svm.core.jni.headers.JNIEnvironment; -import com.oracle.svm.core.jni.headers.JNIFieldId; import com.oracle.svm.core.jni.headers.JNIObjectHandle; -import jdk.internal.misc.Unsafe; -import jdk.vm.ci.meta.JavaKind; - /** * Helper code that is used in generated JNI code via {@code JNIGraphKit}. */ @@ -58,6 +45,7 @@ static int nativeCallPrologue() { return JNIObjectHandles.pushLocalFrame(JNIObjectHandles.NATIVE_CALL_MIN_LOCAL_HANDLE_CAPACITY); } + @Uninterruptible(reason = "Must not throw any exceptions - otherwise, we might leak memory.") static void nativeCallEpilogue(int handleFrame) { JNIObjectHandles.popLocalFramesIncluding(handleFrame); } @@ -76,72 +64,23 @@ static Object unboxHandle(JNIObjectHandle handle) { return JNIObjectHandles.getObject(handle); } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - static WordBase getFieldOffsetFromId(JNIFieldId fieldId) { - return JNIAccessibleField.getOffsetFromId(fieldId); - } - - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - static Object getStaticPrimitiveFieldsArray() { - return StaticFieldsSupport.getStaticPrimitiveFields(); - } - - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - static Object getStaticObjectFieldsArray() { - return StaticFieldsSupport.getStaticObjectFields(); - } - + @Uninterruptible(reason = "Must not throw any exceptions.") static void setPendingException(Throwable t) { JNIThreadLocalPendingException.set(t); } + @Uninterruptible(reason = "Must not throw any exceptions.") static Throwable getAndClearPendingException() { Throwable t = JNIThreadLocalPendingException.get(); JNIThreadLocalPendingException.clear(); return t; } + @Uninterruptible(reason = "Must not throw any exceptions, except for the pending exception.") static void rethrowPendingException() throws Throwable { Throwable t = getAndClearPendingException(); if (t != null) { throw t; } } - - static PointerBase createArrayViewAndGetAddress(Object array, CCharPointer isCopy) throws Throwable { - if (array.getClass().isArray()) { - PrimitiveArrayView ref = JNIThreadLocalPrimitiveArrayViews.createArrayView(array); - if (isCopy.isNonNull()) { - isCopy.write(ref.isCopy() ? (byte) 1 : (byte) 0); - } - return ref.addressOfArrayElement(0); - } - return WordFactory.nullPointer(); - } - - static void destroyNewestArrayViewByAddress(PointerBase address, int mode) throws Throwable { - JNIThreadLocalPrimitiveArrayViews.destroyNewestArrayViewByAddress(address, mode); - } - - static void getPrimitiveArrayRegion(JavaKind elementKind, Object array, int start, int count, PointerBase buffer) { - if (start < 0 || count < 0 || start + count > Array.getLength(array)) { - throw new ArrayIndexOutOfBoundsException(); - } - if (count > 0) { - long offset = ConfigurationValues.getObjectLayout().getArrayElementOffset(elementKind, start); - int elementSize = ConfigurationValues.getObjectLayout().sizeInBytes(elementKind); - Unsafe.getUnsafe().copyMemory(array, offset, null, buffer.rawValue(), count * elementSize); - } - } - - static void setPrimitiveArrayRegion(JavaKind elementKind, Object array, int start, int count, PointerBase buffer) { - if (start < 0 || count < 0 || start + count > Array.getLength(array)) { - throw new ArrayIndexOutOfBoundsException(); - } - if (count > 0) { - long offset = ConfigurationValues.getObjectLayout().getArrayElementOffset(elementKind, start); - int elementSize = ConfigurationValues.getObjectLayout().sizeInBytes(elementKind); - Unsafe.getUnsafe().copyMemory(null, buffer.rawValue(), array, offset, count * elementSize); - } - } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIObjectFieldAccess.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIObjectFieldAccess.java new file mode 100644 index 000000000000..5eeaa9340d1d --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIObjectFieldAccess.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017, 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; + +import org.graalvm.nativeimage.ImageSingletons; + +import com.oracle.svm.core.jni.access.JNIAccessibleField; +import com.oracle.svm.core.jni.headers.JNIFieldId; +import com.oracle.svm.core.jni.headers.JNIObjectHandle; + +import jdk.graal.compiler.api.replacements.Fold; +import jdk.internal.misc.Unsafe; + +public class JNIObjectFieldAccess { + @Fold + public static JNIObjectFieldAccess singleton() { + return ImageSingletons.lookup(JNIObjectFieldAccess.class); + } + + public JNIObjectHandle getObjectField(JNIObjectHandle obj, JNIFieldId fieldId) { + Object o = JNIObjectHandles.getObject(obj); + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + Object result = getObjectField0(o, offset); + return JNIObjectHandles.createLocal(result); + } + + protected Object getObjectField0(Object obj, long offset) { + return Unsafe.getUnsafe().getReference(obj, offset); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIObjectHandles.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIObjectHandles.java index c91eaaab3a8d..66d7e6880f76 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIObjectHandles.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIObjectHandles.java @@ -24,9 +24,6 @@ */ package com.oracle.svm.core.jni; -import jdk.graal.compiler.api.replacements.Fold; -import jdk.graal.compiler.nodes.extended.BranchProbabilityNode; -import jdk.graal.compiler.word.Word; import org.graalvm.nativeimage.CurrentIsolate; import org.graalvm.nativeimage.Isolate; import org.graalvm.nativeimage.ObjectHandle; @@ -48,6 +45,10 @@ import com.oracle.svm.core.threadlocal.FastThreadLocalFactory; import com.oracle.svm.core.threadlocal.FastThreadLocalObject; +import jdk.graal.compiler.api.replacements.Fold; +import jdk.graal.compiler.nodes.extended.BranchProbabilityNode; +import jdk.graal.compiler.word.Word; + /** * Centralized management of {@linkplain JNIObjectHandle JNI handles for Java objects}. There are * different kinds of JNI handles and the main purpose of this class is to abstract their @@ -133,6 +134,10 @@ private static JNIObjectHandle encodeLocal(ObjectHandle handle) { return (JNIObjectHandle) handle; } + /** + * Returns the Java object that is referenced by the given handle. Note that this method may + * execute interruptible code. + */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static T getObject(JNIObjectHandle handle) { if (handle.equal(nullHandle())) { @@ -144,15 +149,15 @@ public static T getObject(JNIObjectHandle handle) { if (useImageHeapHandles() && JNIImageHeapHandles.isInRange(handle)) { return JNIImageHeapHandles.getObject(handle); } - return getObjectSlow(handle); + return getObjectSlowInterruptibly(handle); } @Uninterruptible(reason = "Not really, but our caller is to allow inlining and we must be safe at this point.", calleeMustBe = false) - private static T getObjectSlow(JNIObjectHandle handle) { - return getObjectSlow0(handle); + private static T getObjectSlowInterruptibly(JNIObjectHandle handle) { + return getObjectSlowInterruptibly0(handle); } - private static T getObjectSlow0(JNIObjectHandle handle) { + private static T getObjectSlowInterruptibly0(JNIObjectHandle handle) { if (JNIGlobalHandles.isInRange(handle)) { return JNIGlobalHandles.getObject(handle); } @@ -225,6 +230,7 @@ public static void popLocalFrame() { getExistingLocals().popFrame(); } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static void popLocalFramesIncluding(int frame) { getExistingLocals().popFramesIncluding(frame); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIThreadLocalPendingException.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIThreadLocalPendingException.java index af76ca09dedc..542e7c5ff1ff 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIThreadLocalPendingException.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIThreadLocalPendingException.java @@ -34,6 +34,7 @@ public class JNIThreadLocalPendingException { private static final FastThreadLocalObject pendingException = FastThreadLocalFactory.createObject(Throwable.class, "JNIThreadLocalPendingException.pendingException"); + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static Throwable get() { return pendingException.get(); } @@ -43,6 +44,7 @@ public static void set(Throwable t) { pendingException.set(t); } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static void clear() { set(null); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleMethod.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleMethod.java index e7f22228b31d..9dc59d8e3a8c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleMethod.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleMethod.java @@ -119,12 +119,13 @@ public boolean isNegative() { } @AlwaysInline("Work around an issue with the LLVM backend with which the return value was accessed incorrectly.") - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + @Uninterruptible(reason = "Must not throw any exceptions.", callerMustBe = true) CodePointer getCallWrapperAddress() { return callWrapper; } @AlwaysInline("Work around an issue with the LLVM backend with which the return value was accessed incorrectly.") + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) CodePointer getJavaCallAddress(Object instance, boolean nonVirtual) { if (!nonVirtual) { if (SubstrateOptions.closedTypeWorld()) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIFunctions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIFunctions.java index b564d50b0e30..efb9aae6f004 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIFunctions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/functions/JNIFunctions.java @@ -38,20 +38,26 @@ import org.graalvm.nativeimage.IsolateThread; import org.graalvm.nativeimage.LogHandler; import org.graalvm.nativeimage.c.function.CEntryPoint; +import org.graalvm.nativeimage.c.function.CEntryPoint.FatalExceptionHandler; import org.graalvm.nativeimage.c.function.CEntryPoint.Publish; import org.graalvm.nativeimage.c.function.CFunctionPointer; import org.graalvm.nativeimage.c.function.CodePointer; import org.graalvm.nativeimage.c.function.InvokeCFunctionPointer; import org.graalvm.nativeimage.c.struct.SizeOf; import org.graalvm.nativeimage.c.type.CCharPointer; +import org.graalvm.nativeimage.c.type.CConst; import org.graalvm.nativeimage.c.type.CShortPointer; import org.graalvm.nativeimage.c.type.CTypeConversion; import org.graalvm.nativeimage.c.type.WordPointer; import org.graalvm.word.Pointer; +import org.graalvm.word.PointerBase; +import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordBase; import org.graalvm.word.WordFactory; +import com.oracle.svm.core.JavaMemoryUtil; import com.oracle.svm.core.NeverInline; +import com.oracle.svm.core.StaticFieldsSupport; import com.oracle.svm.core.SubstrateDiagnostics; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.Uninterruptible; @@ -63,16 +69,19 @@ import com.oracle.svm.core.c.function.CEntryPointErrors; import com.oracle.svm.core.c.function.CEntryPointOptions; import com.oracle.svm.core.c.function.CEntryPointOptions.ReturnNullPointer; +import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.graal.stackvalue.UnsafeStackValue; import com.oracle.svm.core.handles.PrimitiveArrayView; import com.oracle.svm.core.heap.RestrictHeapAccess; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.hub.PredefinedClassesSupport; import com.oracle.svm.core.jdk.DirectByteBufferUtil; +import com.oracle.svm.core.jni.JNIObjectFieldAccess; import com.oracle.svm.core.jni.JNIObjectHandles; import com.oracle.svm.core.jni.JNIThreadLocalPendingException; import com.oracle.svm.core.jni.JNIThreadLocalPrimitiveArrayViews; import com.oracle.svm.core.jni.JNIThreadOwnedMonitors; +import com.oracle.svm.core.jni.access.JNIAccessibleField; import com.oracle.svm.core.jni.access.JNIAccessibleMethod; import com.oracle.svm.core.jni.access.JNIAccessibleMethodDescriptor; import com.oracle.svm.core.jni.access.JNINativeLinkage; @@ -113,6 +122,7 @@ import com.oracle.svm.core.thread.Target_java_lang_BaseVirtualThread; import com.oracle.svm.core.thread.Target_jdk_internal_vm_Continuation; import com.oracle.svm.core.thread.VMThreads.SafepointBehavior; +import com.oracle.svm.core.util.ArrayUtil; import com.oracle.svm.core.util.Utf8; import com.oracle.svm.core.util.VMError; @@ -120,6 +130,7 @@ import jdk.graal.compiler.nodes.java.ArrayLengthNode; import jdk.graal.compiler.serviceprovider.JavaVersionUtil; import jdk.internal.misc.Unsafe; +import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.MetaUtil; /** @@ -147,6 +158,7 @@ */ @SuppressWarnings("unused") public final class JNIFunctions { + private static final Unsafe U = Unsafe.getUnsafe(); // Checkstyle: stop @@ -438,7 +450,7 @@ static JNIMethodId GetStaticMethodID(JNIEnvironment env, JNIObjectHandle hclazz, } /* - * jfieldID GetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);\ + * jfieldID GetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig); * * jfieldID GetStaticFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig); */ @@ -463,7 +475,7 @@ static JNIFieldId GetStaticFieldID(JNIEnvironment env, JNIObjectHandle hclazz, C @CEntryPointOptions(prologue = JNIEnvEnterPrologue.class, prologueBailout = ReturnNullHandle.class) static JNIObjectHandle AllocObject(JNIEnvironment env, JNIObjectHandle classHandle) throws InstantiationException { Class clazz = JNIObjectHandles.getObject(classHandle); - Object instance = Unsafe.getUnsafe().allocateInstance(clazz); + Object instance = U.allocateInstance(clazz); return JNIObjectHandles.createLocal(instance); } @@ -1125,6 +1137,501 @@ static boolean IsVirtualThread(JNIEnvironment env, JNIObjectHandle handle) { return obj instanceof Target_java_lang_BaseVirtualThread; } + @CEntryPoint(exceptionHandler = JNIExceptionHandlerReturnNullWord.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static PointerBase GetBooleanArrayElements(JNIEnvironment env, JNIObjectHandle array, CCharPointer isCopy) { + return Support.createArrayViewAndGetAddress(array, isCopy); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerReturnNullWord.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static PointerBase GetByteArrayElements(JNIEnvironment env, JNIObjectHandle array, CCharPointer isCopy) { + return Support.createArrayViewAndGetAddress(array, isCopy); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerReturnNullWord.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static PointerBase GetShortArrayElements(JNIEnvironment env, JNIObjectHandle array, CCharPointer isCopy) { + return Support.createArrayViewAndGetAddress(array, isCopy); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerReturnNullWord.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static PointerBase GetCharArrayElements(JNIEnvironment env, JNIObjectHandle array, CCharPointer isCopy) { + return Support.createArrayViewAndGetAddress(array, isCopy); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerReturnNullWord.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static PointerBase GetIntArrayElements(JNIEnvironment env, JNIObjectHandle array, CCharPointer isCopy) { + return Support.createArrayViewAndGetAddress(array, isCopy); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerReturnNullWord.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static PointerBase GetLongArrayElements(JNIEnvironment env, JNIObjectHandle array, CCharPointer isCopy) { + return Support.createArrayViewAndGetAddress(array, isCopy); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerReturnNullWord.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static PointerBase GetFloatArrayElements(JNIEnvironment env, JNIObjectHandle array, CCharPointer isCopy) { + return Support.createArrayViewAndGetAddress(array, isCopy); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerReturnNullWord.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static PointerBase GetDoubleArrayElements(JNIEnvironment env, JNIObjectHandle array, CCharPointer isCopy) { + return Support.createArrayViewAndGetAddress(array, isCopy); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void ReleaseBooleanArrayElements(JNIEnvironment env, JNIObjectHandle array, PointerBase elements, int mode) { + Support.destroyNewestArrayViewByAddress(elements, mode); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void ReleaseByteArrayElements(JNIEnvironment env, JNIObjectHandle array, PointerBase elements, int mode) { + Support.destroyNewestArrayViewByAddress(elements, mode); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void ReleaseShortArrayElements(JNIEnvironment env, JNIObjectHandle array, PointerBase elements, int mode) { + Support.destroyNewestArrayViewByAddress(elements, mode); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void ReleaseCharArrayElements(JNIEnvironment env, JNIObjectHandle array, PointerBase elements, int mode) { + Support.destroyNewestArrayViewByAddress(elements, mode); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void ReleaseIntArrayElements(JNIEnvironment env, JNIObjectHandle array, PointerBase elements, int mode) { + Support.destroyNewestArrayViewByAddress(elements, mode); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void ReleaseLongArrayElements(JNIEnvironment env, JNIObjectHandle array, PointerBase elements, int mode) { + Support.destroyNewestArrayViewByAddress(elements, mode); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void ReleaseFloatArrayElements(JNIEnvironment env, JNIObjectHandle array, PointerBase elements, int mode) { + Support.destroyNewestArrayViewByAddress(elements, mode); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void ReleaseDoubleArrayElements(JNIEnvironment env, JNIObjectHandle array, PointerBase elements, int mode) { + Support.destroyNewestArrayViewByAddress(elements, mode); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void GetBooleanArrayRegion(JNIEnvironment env, JNIObjectHandle array, int start, int count, PointerBase buffer) { + Support.getPrimitiveArrayRegion(JavaKind.Boolean, array, start, count, buffer); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void GetByteArrayRegion(JNIEnvironment env, JNIObjectHandle array, int start, int count, PointerBase buffer) { + Support.getPrimitiveArrayRegion(JavaKind.Byte, array, start, count, buffer); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void GetShortArrayRegion(JNIEnvironment env, JNIObjectHandle array, int start, int count, PointerBase buffer) { + Support.getPrimitiveArrayRegion(JavaKind.Short, array, start, count, buffer); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void GetCharArrayRegion(JNIEnvironment env, JNIObjectHandle array, int start, int count, PointerBase buffer) { + Support.getPrimitiveArrayRegion(JavaKind.Char, array, start, count, buffer); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void GetIntArrayRegion(JNIEnvironment env, JNIObjectHandle array, int start, int count, PointerBase buffer) { + Support.getPrimitiveArrayRegion(JavaKind.Int, array, start, count, buffer); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void GetLongArrayRegion(JNIEnvironment env, JNIObjectHandle array, int start, int count, PointerBase buffer) { + Support.getPrimitiveArrayRegion(JavaKind.Long, array, start, count, buffer); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void GetFloatArrayRegion(JNIEnvironment env, JNIObjectHandle array, int start, int count, PointerBase buffer) { + Support.getPrimitiveArrayRegion(JavaKind.Float, array, start, count, buffer); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void GetDoubleArrayRegion(JNIEnvironment env, JNIObjectHandle array, int start, int count, PointerBase buffer) { + Support.getPrimitiveArrayRegion(JavaKind.Double, array, start, count, buffer); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetBooleanArrayRegion(JNIEnvironment env, JNIObjectHandle array, int start, int count, @CConst PointerBase buffer) { + Support.setPrimitiveArrayRegion(JavaKind.Boolean, array, start, count, buffer); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetByteArrayRegion(JNIEnvironment env, JNIObjectHandle array, int start, int count, @CConst PointerBase buffer) { + Support.setPrimitiveArrayRegion(JavaKind.Byte, array, start, count, buffer); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetShortArrayRegion(JNIEnvironment env, JNIObjectHandle array, int start, int count, @CConst PointerBase buffer) { + Support.setPrimitiveArrayRegion(JavaKind.Short, array, start, count, buffer); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetCharArrayRegion(JNIEnvironment env, JNIObjectHandle array, int start, int count, @CConst PointerBase buffer) { + Support.setPrimitiveArrayRegion(JavaKind.Char, array, start, count, buffer); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetIntArrayRegion(JNIEnvironment env, JNIObjectHandle array, int start, int count, @CConst PointerBase buffer) { + Support.setPrimitiveArrayRegion(JavaKind.Int, array, start, count, buffer); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetLongArrayRegion(JNIEnvironment env, JNIObjectHandle array, int start, int count, @CConst PointerBase buffer) { + Support.setPrimitiveArrayRegion(JavaKind.Long, array, start, count, buffer); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetFloatArrayRegion(JNIEnvironment env, JNIObjectHandle array, int start, int count, @CConst PointerBase buffer) { + Support.setPrimitiveArrayRegion(JavaKind.Float, array, start, count, buffer); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetDoubleArrayRegion(JNIEnvironment env, JNIObjectHandle array, int start, int count, @CConst PointerBase buffer) { + Support.setPrimitiveArrayRegion(JavaKind.Double, array, start, count, buffer); + } + + /* It is not correct to return null when an exception is thrown, see GR-54276. */ + @CEntryPoint(exceptionHandler = JNIExceptionHandlerReturnNullWord.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static JNIObjectHandle GetObjectField(JNIEnvironment env, JNIObjectHandle obj, JNIFieldId fieldId) { + return JNIObjectFieldAccess.singleton().getObjectField(obj, fieldId); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static boolean GetBooleanField(JNIEnvironment env, JNIObjectHandle obj, JNIFieldId fieldId) { + Object o = JNIObjectHandles.getObject(obj); + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + return U.getBoolean(o, offset); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static byte GetByteField(JNIEnvironment env, JNIObjectHandle obj, JNIFieldId fieldId) { + Object o = JNIObjectHandles.getObject(obj); + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + return U.getByte(o, offset); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static short GetShortField(JNIEnvironment env, JNIObjectHandle obj, JNIFieldId fieldId) { + Object o = JNIObjectHandles.getObject(obj); + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + return U.getShort(o, offset); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static char GetCharField(JNIEnvironment env, JNIObjectHandle obj, JNIFieldId fieldId) { + Object o = JNIObjectHandles.getObject(obj); + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + return U.getChar(o, offset); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static int GetIntField(JNIEnvironment env, JNIObjectHandle obj, JNIFieldId fieldId) { + Object o = JNIObjectHandles.getObject(obj); + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + return U.getInt(o, offset); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static long GetLongField(JNIEnvironment env, JNIObjectHandle obj, JNIFieldId fieldId) { + Object o = JNIObjectHandles.getObject(obj); + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + return U.getLong(o, offset); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static float GetFloatField(JNIEnvironment env, JNIObjectHandle obj, JNIFieldId fieldId) { + Object o = JNIObjectHandles.getObject(obj); + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + return U.getFloat(o, offset); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static double GetDoubleField(JNIEnvironment env, JNIObjectHandle obj, JNIFieldId fieldId) { + Object o = JNIObjectHandles.getObject(obj); + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + return U.getDouble(o, offset); + } + + /* It is not correct to return null when an exception is thrown, see GR-54276. */ + @CEntryPoint(exceptionHandler = JNIExceptionHandlerReturnNullWord.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static JNIObjectHandle GetStaticObjectField(JNIEnvironment env, JNIObjectHandle clazz, JNIFieldId fieldId) { + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + Object result = U.getReference(StaticFieldsSupport.getStaticObjectFields(), offset); + return JNIObjectHandles.createLocal(result); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static boolean GetStaticBooleanField(JNIEnvironment env, JNIObjectHandle clazz, JNIFieldId fieldId) { + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + return U.getBoolean(StaticFieldsSupport.getStaticPrimitiveFields(), offset); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static byte GetStaticByteField(JNIEnvironment env, JNIObjectHandle clazz, JNIFieldId fieldId) { + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + return U.getByte(StaticFieldsSupport.getStaticPrimitiveFields(), offset); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static short GetStaticShortField(JNIEnvironment env, JNIObjectHandle clazz, JNIFieldId fieldId) { + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + return U.getShort(StaticFieldsSupport.getStaticPrimitiveFields(), offset); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static char GetStaticCharField(JNIEnvironment env, JNIObjectHandle clazz, JNIFieldId fieldId) { + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + return U.getChar(StaticFieldsSupport.getStaticPrimitiveFields(), offset); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static int GetStaticIntField(JNIEnvironment env, JNIObjectHandle clazz, JNIFieldId fieldId) { + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + return U.getInt(StaticFieldsSupport.getStaticPrimitiveFields(), offset); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static long GetStaticLongField(JNIEnvironment env, JNIObjectHandle clazz, JNIFieldId fieldId) { + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + return U.getLong(StaticFieldsSupport.getStaticPrimitiveFields(), offset); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static float GetStaticFloatField(JNIEnvironment env, JNIObjectHandle clazz, JNIFieldId fieldId) { + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + return U.getFloat(StaticFieldsSupport.getStaticPrimitiveFields(), offset); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static double GetStaticDoubleField(JNIEnvironment env, JNIObjectHandle clazz, JNIFieldId fieldId) { + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + return U.getDouble(StaticFieldsSupport.getStaticPrimitiveFields(), offset); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetObjectField(JNIEnvironment env, JNIObjectHandle obj, JNIFieldId fieldId, JNIObjectHandle value) { + Object o = JNIObjectHandles.getObject(obj); + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + U.putReference(o, offset, JNIObjectHandles.getObject(value)); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetBooleanField(JNIEnvironment env, JNIObjectHandle obj, JNIFieldId fieldId, boolean value) { + Object o = JNIObjectHandles.getObject(obj); + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + U.putBoolean(o, offset, value); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetByteField(JNIEnvironment env, JNIObjectHandle obj, JNIFieldId fieldId, byte value) { + Object o = JNIObjectHandles.getObject(obj); + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + U.putByte(o, offset, value); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetShortField(JNIEnvironment env, JNIObjectHandle obj, JNIFieldId fieldId, short value) { + Object o = JNIObjectHandles.getObject(obj); + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + U.putShort(o, offset, value); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetCharField(JNIEnvironment env, JNIObjectHandle obj, JNIFieldId fieldId, char value) { + Object o = JNIObjectHandles.getObject(obj); + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + U.putChar(o, offset, value); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetIntField(JNIEnvironment env, JNIObjectHandle obj, JNIFieldId fieldId, int value) { + Object o = JNIObjectHandles.getObject(obj); + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + U.putInt(o, offset, value); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetLongField(JNIEnvironment env, JNIObjectHandle obj, JNIFieldId fieldId, long value) { + Object o = JNIObjectHandles.getObject(obj); + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + U.putLong(o, offset, value); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetFloatField(JNIEnvironment env, JNIObjectHandle obj, JNIFieldId fieldId, float value) { + Object o = JNIObjectHandles.getObject(obj); + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + U.putFloat(o, offset, value); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetDoubleField(JNIEnvironment env, JNIObjectHandle obj, JNIFieldId fieldId, double value) { + Object o = JNIObjectHandles.getObject(obj); + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + U.putDouble(o, offset, value); + } + + @CEntryPoint(exceptionHandler = JNIExceptionHandlerVoid.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetStaticObjectField(JNIEnvironment env, JNIObjectHandle clazz, JNIFieldId fieldId, JNIObjectHandle value) { + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + U.putReference(StaticFieldsSupport.getStaticObjectFields(), offset, JNIObjectHandles.getObject(value)); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetStaticBooleanField(JNIEnvironment env, JNIObjectHandle clazz, JNIFieldId fieldId, boolean value) { + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + U.putBoolean(StaticFieldsSupport.getStaticPrimitiveFields(), offset, value); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetStaticByteField(JNIEnvironment env, JNIObjectHandle clazz, JNIFieldId fieldId, byte value) { + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + U.putByte(StaticFieldsSupport.getStaticPrimitiveFields(), offset, value); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetStaticShortField(JNIEnvironment env, JNIObjectHandle clazz, JNIFieldId fieldId, short value) { + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + U.putShort(StaticFieldsSupport.getStaticPrimitiveFields(), offset, value); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetStaticCharField(JNIEnvironment env, JNIObjectHandle clazz, JNIFieldId fieldId, char value) { + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + U.putChar(StaticFieldsSupport.getStaticPrimitiveFields(), offset, value); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetStaticIntField(JNIEnvironment env, JNIObjectHandle clazz, JNIFieldId fieldId, int value) { + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + U.putInt(StaticFieldsSupport.getStaticPrimitiveFields(), offset, value); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetStaticLongField(JNIEnvironment env, JNIObjectHandle clazz, JNIFieldId fieldId, long value) { + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + U.putLong(StaticFieldsSupport.getStaticPrimitiveFields(), offset, value); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetStaticFloatField(JNIEnvironment env, JNIObjectHandle clazz, JNIFieldId fieldId, float value) { + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + U.putFloat(StaticFieldsSupport.getStaticPrimitiveFields(), offset, value); + } + + @Uninterruptible(reason = "Must not throw any exceptions.") + @CEntryPoint(exceptionHandler = FatalExceptionHandler.class, include = CEntryPoint.NotIncludedAutomatically.class, publishAs = Publish.NotPublished) + @CEntryPointOptions(prologue = JNIEnvEnterFatalOnFailurePrologue.class) + static void SetStaticDoubleField(JNIEnvironment env, JNIObjectHandle clazz, JNIFieldId fieldId, double value) { + long offset = JNIAccessibleField.getOffsetFromId(fieldId).rawValue(); + U.putDouble(StaticFieldsSupport.getStaticPrimitiveFields(), offset, value); + } + // Checkstyle: resume /** @@ -1434,6 +1941,51 @@ static Target_java_nio_Buffer directBufferFromJNIHandle(JNIObjectHandle handle) return null; } } + + static PointerBase createArrayViewAndGetAddress(JNIObjectHandle handle, CCharPointer isCopy) { + Object obj = JNIObjectHandles.getObject(handle); + if (!obj.getClass().isArray()) { + throw new IllegalArgumentException("Argument is not an array"); + } + + /* Create a view for the non-null array object. */ + PrimitiveArrayView ref = JNIThreadLocalPrimitiveArrayViews.createArrayView(obj); + if (isCopy.isNonNull()) { + isCopy.write(ref.isCopy() ? (byte) 1 : (byte) 0); + } + return ref.addressOfArrayElement(0); + } + + static void destroyNewestArrayViewByAddress(PointerBase address, int mode) { + JNIThreadLocalPrimitiveArrayViews.destroyNewestArrayViewByAddress(address, mode); + } + + static void getPrimitiveArrayRegion(JavaKind elementKind, JNIObjectHandle handle, int start, int count, PointerBase buffer) { + Object obj = JNIObjectHandles.getObject(handle); + /* Check if we have a non-null array object and if start/count are valid. */ + if (ArrayUtil.isOutOfBounds(obj, start, count)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (count > 0) { + long offset = ConfigurationValues.getObjectLayout().getArrayElementOffset(elementKind, start); + int elementSize = ConfigurationValues.getObjectLayout().sizeInBytes(elementKind); + UnsignedWord bytes = WordFactory.unsigned(count).multiply(elementSize); + JavaMemoryUtil.copyOnHeap(obj, WordFactory.unsigned(offset), null, WordFactory.unsigned(buffer.rawValue()), bytes); + } + } + + static void setPrimitiveArrayRegion(JavaKind elementKind, JNIObjectHandle handle, int start, int count, PointerBase buffer) { + Object obj = JNIObjectHandles.getObject(handle); + if (ArrayUtil.isOutOfBounds(obj, start, count)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (count > 0) { + long offset = ConfigurationValues.getObjectLayout().getArrayElementOffset(elementKind, start); + int elementSize = ConfigurationValues.getObjectLayout().sizeInBytes(elementKind); + UnsignedWord bytes = WordFactory.unsigned(count).multiply(elementSize); + JavaMemoryUtil.copyOnHeap(null, WordFactory.unsigned(buffer.rawValue()), obj, WordFactory.unsigned(offset), bytes); + } + } } static final CGlobalData UNIMPLEMENTED_UNATTACHED_ERROR_MESSAGE = CGlobalDataFactory.createCString( diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/headers/JNIFunctionPointerTypes.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/headers/JNIFunctionPointerTypes.java index 4b944d8bfd62..95420cac519d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/headers/JNIFunctionPointerTypes.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/headers/JNIFunctionPointerTypes.java @@ -222,6 +222,11 @@ public interface ReleaseByteArrayElementsFunctionPointer extends CFunctionPointe CCharPointer invoke(JNIEnvironment env, JNIObjectHandle byteArray, CCharPointer elements, int mode); } + public interface GetIntFieldFunctionPointer extends CFunctionPointer { + @InvokeCFunctionPointer + int invoke(JNIEnvironment env, JNIObjectHandle obj, JNIFieldId fieldId); + } + public interface GetObjectFieldFunctionPointer extends CFunctionPointer { @InvokeCFunctionPointer JNIObjectHandle invoke(JNIEnvironment env, JNIObjectHandle obj, JNIFieldId fieldId); @@ -237,6 +242,11 @@ public interface CallStaticObjectMethodAFunctionPointer extends CFunctionPointer JNIObjectHandle invoke(JNIEnvironment env, JNIObjectHandle clazz, JNIMethodId methodID, JNIValue args); } + public interface GetStaticIntFieldFunctionPointer extends CFunctionPointer { + @InvokeCFunctionPointer + int invoke(JNIEnvironment env, JNIObjectHandle clazz, JNIFieldId fieldID); + } + public interface GetStaticObjectFieldFunctionPointer extends CFunctionPointer { @InvokeCFunctionPointer JNIObjectHandle invoke(JNIEnvironment env, JNIObjectHandle clazz, JNIFieldId fieldID); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/headers/JNINativeInterface.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/headers/JNINativeInterface.java index 893c2a6af728..3351724ed326 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/headers/JNINativeInterface.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/headers/JNINativeInterface.java @@ -51,10 +51,12 @@ import com.oracle.svm.core.jni.headers.JNIFunctionPointerTypes.GetBooleanFieldFunctionPointer; import com.oracle.svm.core.jni.headers.JNIFunctionPointerTypes.GetByteArrayElementsFunctionPointer; import com.oracle.svm.core.jni.headers.JNIFunctionPointerTypes.GetFieldIDFunctionPointer; +import com.oracle.svm.core.jni.headers.JNIFunctionPointerTypes.GetIntFieldFunctionPointer; import com.oracle.svm.core.jni.headers.JNIFunctionPointerTypes.GetMethodIDFunctionPointer; import com.oracle.svm.core.jni.headers.JNIFunctionPointerTypes.GetObjectArrayElementFunctionPointer; import com.oracle.svm.core.jni.headers.JNIFunctionPointerTypes.GetObjectClassFunctionPointer; import com.oracle.svm.core.jni.headers.JNIFunctionPointerTypes.GetObjectFieldFunctionPointer; +import com.oracle.svm.core.jni.headers.JNIFunctionPointerTypes.GetStaticIntFieldFunctionPointer; import com.oracle.svm.core.jni.headers.JNIFunctionPointerTypes.GetStaticObjectFieldFunctionPointer; import com.oracle.svm.core.jni.headers.JNIFunctionPointerTypes.GetStringUTFCharsFunctionPointer; import com.oracle.svm.core.jni.headers.JNIFunctionPointerTypes.GetSuperclassFunctionPointer; @@ -668,10 +670,10 @@ public interface JNINativeInterface extends PointerBase { void setGetShortField(CFunctionPointer p); @CField - CFunctionPointer getGetIntField(); + GetIntFieldFunctionPointer getGetIntField(); @CField - void setGetIntField(CFunctionPointer p); + void setGetIntField(GetIntFieldFunctionPointer p); @CField CFunctionPointer getGetLongField(); @@ -968,10 +970,10 @@ public interface JNINativeInterface extends PointerBase { void setGetStaticShortField(CFunctionPointer p); @CField - CFunctionPointer getGetStaticIntField(); + GetStaticIntFieldFunctionPointer getGetStaticIntField(); @CField - void setGetStaticIntField(CFunctionPointer p); + void setGetStaticIntField(GetStaticIntFieldFunctionPointer p); @CField CFunctionPointer getGetStaticLongField(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/ExceptionUnwind.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/ExceptionUnwind.java index 4def61b84719..993c09a8bca9 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/ExceptionUnwind.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/ExceptionUnwind.java @@ -213,6 +213,7 @@ private static void defaultUnwindException(Pointer startSP, boolean fromMethodWi } /* No handler found in this frame, walk to caller frame. */ + VMError.guarantee(!JavaFrames.isEntryPoint(frame), "Entry point methods must have an exception handler."); hasCalleeSavedRegisters = CodeInfoQueryResult.hasCalleeSavedRegisters(frame.getEncodedFrameSize()); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/ImplicitExceptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/ImplicitExceptions.java index 458aa82089d4..c5729cbafb1e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/ImplicitExceptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/ImplicitExceptions.java @@ -24,6 +24,7 @@ */ package com.oracle.svm.core.snippets; +import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE; import static jdk.graal.compiler.core.common.spi.ForeignCallDescriptor.CallSideEffect.HAS_SIDE_EFFECT; import static jdk.graal.compiler.core.common.spi.ForeignCallDescriptor.CallSideEffect.NO_SIDE_EFFECT; @@ -31,8 +32,8 @@ import com.oracle.svm.core.SubstrateDiagnostics; import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.code.FactoryMethodMarker; -import com.oracle.svm.core.heap.RestrictHeapAccess; import com.oracle.svm.core.jdk.InternalVMMethod; import com.oracle.svm.core.jdk.StackTraceUtils; import com.oracle.svm.core.option.SubstrateOptionsParser; @@ -47,6 +48,9 @@ * * All methods in this class are an implementation detail that should not be observable by users, * therefore these methods are filtered in exception stack traces (see {@link StackTraceUtils}). + * + * All methods that retrieve or throw a cached exception are marked as {@link Uninterruptible} + * because they must not throw a StackOverflowError if they are invoked from interruptible code. */ @InternalVMMethod @FactoryMethodMarker @@ -201,8 +205,9 @@ public static void deactivateImplicitExceptionsAreFatal() { implicitExceptionsAreFatal.set(implicitExceptionsAreFatal.get() - 1); } + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static void vmErrorIfImplicitExceptionsAreFatal(boolean cachedException) { - if (cachedException && SubstrateOptions.ImplicitExceptionWithoutStacktraceIsFatal.getValue()) { + if (cachedException && SubstrateDiagnostics.Options.implicitExceptionWithoutStacktraceIsFatal()) { throw VMError.shouldNotReachHere("AssertionError without stack trace."); } else if ((implicitExceptionsAreFatal.get() > 0 || ExceptionUnwind.exceptionsAreFatal()) && !SubstrateDiagnostics.isFatalErrorHandlingThread()) { throw VMError.shouldNotReachHere("Implicit exception thrown in code where such exceptions are fatal errors"); @@ -524,7 +529,7 @@ private static void throwOptIncompatibleClassChangeError() { } /** Foreign call: {@link #GET_CACHED_NULL_POINTER_EXCEPTION}. */ - @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Called to report an implicit exception in code that must not allocate.") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @SubstrateForeignCallTarget(stubCallingConvention = true) private static NullPointerException getCachedNullPointerException() { vmErrorIfImplicitExceptionsAreFatal(true); @@ -532,7 +537,7 @@ private static NullPointerException getCachedNullPointerException() { } /** Foreign call: {@link #GET_CACHED_OUT_OF_BOUNDS_EXCEPTION}. */ - @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Called to report an implicit exception in code that must not allocate.") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @SubstrateForeignCallTarget(stubCallingConvention = true) private static ArrayIndexOutOfBoundsException getCachedOutOfBoundsException() { vmErrorIfImplicitExceptionsAreFatal(true); @@ -540,7 +545,7 @@ private static ArrayIndexOutOfBoundsException getCachedOutOfBoundsException() { } /** Foreign call: {@link #GET_CACHED_CLASS_CAST_EXCEPTION}. */ - @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Called to report an implicit exception in code that must not allocate.") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @SubstrateForeignCallTarget(stubCallingConvention = true) private static ClassCastException getCachedClassCastException() { vmErrorIfImplicitExceptionsAreFatal(true); @@ -548,7 +553,7 @@ private static ClassCastException getCachedClassCastException() { } /** Foreign call: {@link #GET_CACHED_ARRAY_STORE_EXCEPTION}. */ - @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Called to report an implicit exception in code that must not allocate.") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @SubstrateForeignCallTarget(stubCallingConvention = true) private static ArrayStoreException getCachedArrayStoreException() { vmErrorIfImplicitExceptionsAreFatal(true); @@ -556,7 +561,7 @@ private static ArrayStoreException getCachedArrayStoreException() { } /** Foreign call: {@link #GET_CACHED_INCOMPATIBLE_CLASS_CHANGE_ERROR}. */ - @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Called to report an implicit exception in code that must not allocate.") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @SubstrateForeignCallTarget(stubCallingConvention = true) private static IncompatibleClassChangeError getCachedIncompatibleClassChangeError() { vmErrorIfImplicitExceptionsAreFatal(true); @@ -564,7 +569,7 @@ private static IncompatibleClassChangeError getCachedIncompatibleClassChangeErro } /** Foreign call: {@link #GET_CACHED_ILLEGAL_ARGUMENT_EXCEPTION}. */ - @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Called to report an implicit exception in code that must not allocate.") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @SubstrateForeignCallTarget(stubCallingConvention = true) private static IllegalArgumentException getCachedIllegalArgumentException() { vmErrorIfImplicitExceptionsAreFatal(true); @@ -572,7 +577,7 @@ private static IllegalArgumentException getCachedIllegalArgumentException() { } /** Foreign call: {@link #GET_CACHED_NEGATIVE_ARRAY_SIZE_EXCEPTION}. */ - @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Called to report an implicit exception in code that must not allocate.") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @SubstrateForeignCallTarget(stubCallingConvention = true) private static NegativeArraySizeException getCachedNegativeArraySizeException() { vmErrorIfImplicitExceptionsAreFatal(true); @@ -580,7 +585,7 @@ private static NegativeArraySizeException getCachedNegativeArraySizeException() } /** Foreign call: {@link #GET_CACHED_ARITHMETIC_EXCEPTION}. */ - @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Called to report an implicit exception in code that must not allocate.") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @SubstrateForeignCallTarget(stubCallingConvention = true) private static ArithmeticException getCachedArithmeticException() { vmErrorIfImplicitExceptionsAreFatal(true); @@ -588,7 +593,7 @@ private static ArithmeticException getCachedArithmeticException() { } /** Foreign call: {@link #GET_CACHED_ASSERTION_ERROR}. */ - @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Called to report an implicit exception in code that must not allocate.") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @SubstrateForeignCallTarget(stubCallingConvention = true) private static AssertionError getCachedAssertionError() { vmErrorIfImplicitExceptionsAreFatal(true); @@ -596,7 +601,7 @@ private static AssertionError getCachedAssertionError() { } /** Foreign call: {@link #GET_CACHED_ILLEGAL_MONITOR_STATE_EXCEPTION}. */ - @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Called to report an implicit exception in code that must not allocate.") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @SubstrateForeignCallTarget(stubCallingConvention = true) private static IllegalMonitorStateException getCachedIllegalMonitorStateException() { vmErrorIfImplicitExceptionsAreFatal(true); @@ -604,7 +609,7 @@ private static IllegalMonitorStateException getCachedIllegalMonitorStateExceptio } /** Foreign call: {@link #THROW_CACHED_NULL_POINTER_EXCEPTION}. */ - @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Called to report an implicit exception in code that must not allocate.") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @SubstrateForeignCallTarget(stubCallingConvention = true) private static void throwCachedNullPointerException() { vmErrorIfImplicitExceptionsAreFatal(true); @@ -612,7 +617,7 @@ private static void throwCachedNullPointerException() { } /** Foreign call: {@link #THROW_CACHED_OUT_OF_BOUNDS_EXCEPTION}. */ - @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Called to report an implicit exception in code that must not allocate.") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @SubstrateForeignCallTarget(stubCallingConvention = true) private static void throwCachedOutOfBoundsException() { vmErrorIfImplicitExceptionsAreFatal(true); @@ -620,7 +625,7 @@ private static void throwCachedOutOfBoundsException() { } /** Foreign call: {@link #THROW_CACHED_CLASS_CAST_EXCEPTION}. */ - @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Called to report an implicit exception in code that must not allocate.") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @SubstrateForeignCallTarget(stubCallingConvention = true) private static void throwCachedClassCastException() { vmErrorIfImplicitExceptionsAreFatal(true); @@ -628,7 +633,7 @@ private static void throwCachedClassCastException() { } /** Foreign call: {@link #THROW_CACHED_ARRAY_STORE_EXCEPTION}. */ - @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Called to report an implicit exception in code that must not allocate.") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @SubstrateForeignCallTarget(stubCallingConvention = true) private static void throwCachedArrayStoreException() { vmErrorIfImplicitExceptionsAreFatal(true); @@ -636,7 +641,7 @@ private static void throwCachedArrayStoreException() { } /** Foreign call: {@link #THROW_CACHED_INCOMPATIBLE_CLASS_CHANGE_ERROR}. */ - @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Called to report an implicit exception in code that must not allocate.") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @SubstrateForeignCallTarget(stubCallingConvention = true) private static void throwCachedIncompatibleClassChangeError() { vmErrorIfImplicitExceptionsAreFatal(true); @@ -644,7 +649,7 @@ private static void throwCachedIncompatibleClassChangeError() { } /** Foreign call: {@link #THROW_CACHED_ILLEGAL_ARGUMENT_EXCEPTION}. */ - @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Called to report an implicit exception in code that must not allocate.") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @SubstrateForeignCallTarget(stubCallingConvention = true) private static void throwCachedIllegalArgumentException() { vmErrorIfImplicitExceptionsAreFatal(true); @@ -652,7 +657,7 @@ private static void throwCachedIllegalArgumentException() { } /** Foreign call: {@link #THROW_CACHED_NEGATIVE_ARRAY_SIZE_EXCEPTION}. */ - @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Called to report an implicit exception in code that must not allocate.") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @SubstrateForeignCallTarget(stubCallingConvention = true) private static void throwCachedNegativeArraySizeException() { vmErrorIfImplicitExceptionsAreFatal(true); @@ -660,7 +665,7 @@ private static void throwCachedNegativeArraySizeException() { } /** Foreign call: {@link #THROW_CACHED_ARITHMETIC_EXCEPTION}. */ - @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Called to report an implicit exception in code that must not allocate.") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @SubstrateForeignCallTarget(stubCallingConvention = true) private static void throwCachedArithmeticException() { vmErrorIfImplicitExceptionsAreFatal(true); @@ -668,7 +673,7 @@ private static void throwCachedArithmeticException() { } /** Foreign call: {@link #THROW_CACHED_ASSERTION_ERROR}. */ - @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Called to report an implicit exception in code that must not allocate.") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @SubstrateForeignCallTarget(stubCallingConvention = true) private static void throwCachedAssertionError() { vmErrorIfImplicitExceptionsAreFatal(true); @@ -676,7 +681,7 @@ private static void throwCachedAssertionError() { } /** Foreign call: {@link #THROW_CACHED_ILLEGAL_MONITOR_STATE_EXCEPTION}. */ - @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Called to report an implicit exception in code that must not allocate.") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) @SubstrateForeignCallTarget(stubCallingConvention = true) private static void throwCachedIllegalMonitorStateException() { vmErrorIfImplicitExceptionsAreFatal(true); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/JavaFrameAnchors.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/JavaFrameAnchors.java index 0dd5c78808ef..e1bd34ad1e9e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/JavaFrameAnchors.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/JavaFrameAnchors.java @@ -29,19 +29,23 @@ import org.graalvm.nativeimage.CurrentIsolate; import org.graalvm.nativeimage.IsolateThread; +import org.graalvm.word.Pointer; import org.graalvm.word.WordFactory; +import com.oracle.svm.core.SubstrateDiagnostics; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.graal.meta.SubstrateForeignCallsProvider; +import com.oracle.svm.core.snippets.KnownIntrinsics; import com.oracle.svm.core.snippets.SnippetRuntime; import com.oracle.svm.core.snippets.SubstrateForeignCallTarget; import com.oracle.svm.core.thread.VMOperation; import com.oracle.svm.core.thread.VMThreads.StatusSupport; import com.oracle.svm.core.threadlocal.FastThreadLocal; import com.oracle.svm.core.threadlocal.FastThreadLocalFactory; +import com.oracle.svm.core.threadlocal.FastThreadLocalInt; import com.oracle.svm.core.threadlocal.FastThreadLocalWord; import com.oracle.svm.core.util.VMError; @@ -58,6 +62,7 @@ public class JavaFrameAnchors { static final SnippetRuntime.SubstrateForeignCallDescriptor VERIFY_FRAME_ANCHOR_STUB = SnippetRuntime.findForeignCall(JavaFrameAnchors.class, "verifyFrameAnchorStub", NO_SIDE_EFFECT); private static final FastThreadLocalWord lastAnchorTL = FastThreadLocalFactory.createWord("JavaFrameAnchors.lastAnchor").setMaxOffset(FastThreadLocal.BYTE_OFFSET); + private static final FastThreadLocalInt verificationInProgressTL = FastThreadLocalFactory.createInt("JavaFrameAnchors.verificationInProgress"); public static void pushFrameAnchor(JavaFrameAnchor newAnchor) { if (SubstrateOptions.VerifyFrameAnchors.getValue()) { @@ -117,18 +122,28 @@ private static void verifyFrameAnchor(boolean newAnchor) { @SubstrateForeignCallTarget(stubCallingConvention = false, fullyUninterruptible = true) @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static void verifyFrameAnchorStub(boolean newAnchor) { - JavaFrameAnchor cur = lastAnchorTL.get(); - verifyFrameAnchor(cur, newAnchor); + if (verificationInProgressTL.get() != 0 || SubstrateDiagnostics.isFatalErrorHandlingThread()) { + return; + } - JavaFrameAnchor prev = cur.getPreviousAnchor(); - if (prev.isNonNull()) { - verifyFrameAnchor(prev, false); + verificationInProgressTL.set(verificationInProgressTL.get() + 1); + try { + JavaFrameAnchor cur = lastAnchorTL.get(); + verifyFrameAnchor(cur, newAnchor); + + JavaFrameAnchor prev = cur.getPreviousAnchor(); + if (prev.isNonNull()) { + verifyFrameAnchor(prev, false); + } + } finally { + verificationInProgressTL.set(verificationInProgressTL.get() - 1); } } @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static void verifyFrameAnchor(JavaFrameAnchor cur, boolean newAnchor) { VMError.guarantee(StatusSupport.getStatusVolatile() == StatusSupport.STATUS_IN_JAVA, "Invalid thread status."); + VMError.guarantee(((Pointer) cur).aboveOrEqual(KnownIntrinsics.readStackPointer()), "The frame anchor struct is outside of the used stack."); VMError.guarantee(cur.getMagicBefore() == JavaFrameAnchor.MAGIC, "Corrupt frame anchor: magic before"); VMError.guarantee(cur.getMagicAfter() == JavaFrameAnchor.MAGIC, "Corrupt frame anchor: magic after"); VMError.guarantee(newAnchor == cur.getLastJavaIP().isNull(), "Corrupt frame anchor: invalid IP"); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAutomaticFeature.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/ArrayUtil.java similarity index 54% rename from substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAutomaticFeature.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/ArrayUtil.java index 5d13835b3ca2..0f95d9eedd88 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAutomaticFeature.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/ArrayUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -22,32 +22,24 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.svm.hosted.jni; +package com.oracle.svm.core.util; -import java.util.Collections; -import java.util.List; +import java.lang.reflect.Array; -import org.graalvm.nativeimage.Platforms; -import org.graalvm.nativeimage.hosted.Feature; -import org.graalvm.nativeimage.impl.InternalPlatform; +import jdk.graal.compiler.nodes.java.ArrayLengthNode; -import com.oracle.svm.core.SubstrateOptions; -import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; -import com.oracle.svm.core.feature.InternalFeature; - -/** - * Automatically enables {@link JNIFeature} when specific options are set. - */ -@AutomaticallyRegisteredFeature -@Platforms(InternalPlatform.NATIVE_ONLY.class) -public class JNIAutomaticFeature implements InternalFeature { - @Override - public boolean isInConfiguration(IsInConfigurationAccess access) { - return SubstrateOptions.JNI.getValue(); +public class ArrayUtil { + public static boolean isOutOfBounds(Object array, int start, int count) { + return start < 0 || count < 0 || start > Array.getLength(array) - count; } - @Override - public List> getRequiredFeatures() { - return Collections.singletonList(JNIFeature.class); + /** + * Should only be used from snippets because this code uses {@link ArrayLengthNode#arrayLength}, + * which doesn't check if the object is really an array. + */ + public static void boundsCheckInSnippet(Object fromArray, int fromIndex, Object toArray, int toIndex, int length) { + if (fromIndex < 0 || toIndex < 0 || length < 0 || fromIndex > ArrayLengthNode.arrayLength(fromArray) - length || toIndex > ArrayLengthNode.arrayLength(toArray) - length) { + throw new ArrayIndexOutOfBoundsException(); + } } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java index 3b404f6ac72e..072afca74b73 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java @@ -288,13 +288,6 @@ public void registerConstructorLookup(ConfigurationCondition condition, Class @Override public void beforeAnalysis(BeforeAnalysisAccess arg) { - if (!ImageSingletons.contains(JNIFieldAccessorMethod.Factory.class)) { - ImageSingletons.add(JNIFieldAccessorMethod.Factory.class, new JNIFieldAccessorMethod.Factory()); - } - if (!ImageSingletons.contains(JNIJavaCallWrapperMethod.Factory.class)) { - ImageSingletons.add(JNIJavaCallWrapperMethod.Factory.class, new JNIJavaCallWrapperMethod.Factory()); - } - BeforeAnalysisAccessImpl access = (BeforeAnalysisAccessImpl) arg; for (CallVariant variant : CallVariant.values()) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIFeature.java index 432de9ded85a..ccf8b573cd08 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIFeature.java @@ -27,7 +27,15 @@ import java.util.Arrays; import java.util.List; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.hosted.Feature; +import org.graalvm.nativeimage.impl.InternalPlatform; + +import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; +import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.core.jni.JNIObjectFieldAccess; /** * Support for the Java Native Interface (JNI). Read more in JNI.md in the project's root directory. @@ -35,10 +43,26 @@ * @see Java Native Interface * Specification */ -public class JNIFeature implements Feature { +@AutomaticallyRegisteredFeature +@Platforms(InternalPlatform.NATIVE_ONLY.class) +public class JNIFeature implements InternalFeature { + @Override + public boolean isInConfiguration(IsInConfigurationAccess access) { + return SubstrateOptions.JNI.getValue(); + } @Override public List> getRequiredFeatures() { return Arrays.asList(JNIFunctionTablesFeature.class, JNICallWrapperFeature.class, JNILibraryLoadFeature.class); } + + @Override + public void afterRegistration(AfterRegistrationAccess access) { + registerSingletons(); + } + + protected void registerSingletons() { + ImageSingletons.add(JNIObjectFieldAccess.class, new JNIObjectFieldAccess()); + ImageSingletons.add(JNIJavaCallWrapperMethod.Factory.class, new JNIJavaCallWrapperMethod.Factory()); + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIFieldAccessorMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIFieldAccessorMethod.java deleted file mode 100644 index 9543c42e7490..000000000000 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIFieldAccessorMethod.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (c) 2017, 2023, 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.hosted.jni; - -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.List; - -import org.graalvm.nativeimage.c.function.CEntryPoint.FatalExceptionHandler; -import org.graalvm.nativeimage.c.function.CEntryPoint.Publish; -import org.graalvm.word.LocationIdentity; - -import com.oracle.graal.pointsto.infrastructure.ResolvedSignature; -import com.oracle.graal.pointsto.meta.AnalysisMethod; -import com.oracle.graal.pointsto.meta.HostedProviders; -import com.oracle.svm.core.c.function.CEntryPointOptions.AutomaticPrologueBailout; -import com.oracle.svm.core.c.function.CEntryPointOptions.NoEpilogue; -import com.oracle.svm.core.c.function.CEntryPointOptions.NoPrologue; -import com.oracle.svm.core.graal.nodes.CEntryPointEnterNode; -import com.oracle.svm.core.graal.nodes.CEntryPointLeaveNode; -import com.oracle.svm.core.graal.nodes.CEntryPointLeaveNode.LeaveAction; -import com.oracle.svm.core.jni.headers.JNIEnvironment; -import com.oracle.svm.core.jni.headers.JNIFieldId; -import com.oracle.svm.core.jni.headers.JNIObjectHandle; -import com.oracle.svm.core.util.VMError; -import com.oracle.svm.hosted.code.CEntryPointData; -import com.oracle.svm.hosted.code.EntryPointCallStubMethod; - -import jdk.graal.compiler.debug.DebugContext; -import jdk.graal.compiler.java.FrameStateBuilder; -import jdk.graal.compiler.nodes.StructuredGraph; -import jdk.graal.compiler.nodes.ValueNode; -import jdk.graal.compiler.nodes.extended.RawLoadNode; -import jdk.graal.compiler.nodes.extended.RawStoreNode; -import jdk.vm.ci.meta.ConstantPool; -import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaType; - -/** - * Generated method for accessing a field via JNI. An accessor is specific to the {@link JavaKind - * basic type} of the field, to static or non-static fields, and can either read or write the field. - * - * The generated method implements one of the following JNI functions: - * - *
    - *
  • {@code GetObjectField}
  • - *
  • {@code GetBooleanField}
  • - *
  • {@code GetByteField}
  • - *
  • {@code GetCharField}
  • - *
  • {@code GetShortField}
  • - *
  • {@code GetIntField}
  • - *
  • {@code GetLongField}
  • - *
  • {@code GetFloatField}
  • - *
  • {@code GetDoubleField}
  • - *
  • {@code SetObjectField}
  • - *
  • {@code SetBooleanField}
  • - *
  • {@code SetByteField}
  • - *
  • {@code SetCharField}
  • - *
  • {@code SetShortField}
  • - *
  • {@code SetIntField}
  • - *
  • {@code SetLongField}
  • - *
  • {@code SetFloatField}
  • - *
  • {@code SetDoubleField}
  • - *
  • {@code GetStaticObjectField}
  • - *
  • {@code GetStaticBooleanField}
  • - *
  • {@code GetStaticByteField}
  • - *
  • {@code GetStaticCharField}
  • - *
  • {@code GetStaticShortField}
  • - *
  • {@code GetStaticIntField}
  • - *
  • {@code GetStaticLongField}
  • - *
  • {@code GetStaticFloatField}
  • - *
  • {@code GetStaticDoubleField}
  • - *
  • {@code SetStaticObjectField}
  • - *
  • {@code SetStaticBooleanField}
  • - *
  • {@code SetStaticByteField}
  • - *
  • {@code SetStaticCharField}
  • - *
  • {@code SetStaticShortField}
  • - *
  • {@code SetStaticIntField}
  • - *
  • {@code SetStaticLongField}
  • - *
  • {@code SetStaticFloatField}
  • - *
  • {@code SetStaticDoubleField}
  • - *
- * - * @see JNI - * Functions - */ -public class JNIFieldAccessorMethod extends EntryPointCallStubMethod { - - public static class Factory { - public JNIFieldAccessorMethod create(JavaKind kind, boolean isSetter, boolean isStatic, ResolvedJavaType generatedMethodClass, ConstantPool constantPool, - MetaAccessProvider wrappedMetaAccess) { - return new JNIFieldAccessorMethod(kind, isSetter, isStatic, generatedMethodClass, constantPool, wrappedMetaAccess); - } - } - - protected final JavaKind fieldKind; - protected final boolean isSetter; - protected final boolean isStatic; - - protected JNIFieldAccessorMethod(JavaKind fieldKind, boolean isSetter, boolean isStatic, ResolvedJavaType declaringClass, ConstantPool constantPool, MetaAccessProvider metaAccess) { - super(createName(fieldKind, isSetter, isStatic), declaringClass, createSignature(fieldKind, isSetter, metaAccess), constantPool); - if (!EnumSet.of(JavaKind.Object, JavaKind.Boolean, JavaKind.Byte, JavaKind.Char, JavaKind.Short, - JavaKind.Int, JavaKind.Long, JavaKind.Float, JavaKind.Double).contains(fieldKind)) { - - throw VMError.shouldNotReachHereUnexpectedInput(fieldKind); // ExcludeFromJacocoGeneratedReport - } - this.fieldKind = fieldKind; - this.isSetter = isSetter; - this.isStatic = isStatic; - } - - private static String createName(JavaKind fieldKind, boolean isSetter, boolean isStatic) { - StringBuilder sb = new StringBuilder(32); - if (isSetter) { - sb.append("Set"); - } else { - sb.append("Get"); - } - if (isStatic) { - sb.append("Static"); - } - sb.append(fieldKind.name()); - sb.append("Field"); - return sb.toString(); - } - - private static ResolvedSignature createSignature(JavaKind fieldKind, boolean isSetter, MetaAccessProvider metaAccess) { - Class valueClass = fieldKind.toJavaClass(); - if (fieldKind.isObject()) { - valueClass = JNIObjectHandle.class; - } - ResolvedJavaType objectHandle = metaAccess.lookupJavaType(JNIObjectHandle.class); - List args = new ArrayList<>(); - args.add(metaAccess.lookupJavaType(JNIEnvironment.class)); - args.add(objectHandle); // this (instance field) or class (static field) - args.add(metaAccess.lookupJavaType(JNIFieldId.class)); - if (isSetter) { - args.add(metaAccess.lookupJavaType(valueClass)); - } - ResolvedJavaType returnType; - if (isSetter) { - returnType = metaAccess.lookupJavaType(Void.TYPE); - } else { - returnType = metaAccess.lookupJavaType(valueClass); - } - return ResolvedSignature.fromList(args, returnType); - } - - @Override - public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, HostedProviders providers, Purpose purpose) { - JNIGraphKit kit = new JNIGraphKit(debug, providers, method); - - ValueNode vmThread = kit.loadLocal(0, getSignature().getParameterKind(0)); - kit.append(CEntryPointEnterNode.enter(vmThread)); - - ValueNode returnValue = buildGraphBody(kit, kit.getInitialArguments(), kit.getFrameState(), kit.getMetaAccess()); - - kit.appendStateSplitProxy(); - CEntryPointLeaveNode leave = new CEntryPointLeaveNode(LeaveAction.Leave); - kit.append(leave); - JavaKind returnKind = isSetter ? JavaKind.Void : fieldKind; - kit.createReturn(returnValue, returnKind); - - return kit.finalizeGraph(); - } - - protected ValueNode buildGraphBody(JNIGraphKit kit, List arguments, @SuppressWarnings("unused") FrameStateBuilder state, @SuppressWarnings("unused") MetaAccessProvider metaAccess) { - ValueNode object; - if (isStatic) { - if (fieldKind.isPrimitive()) { - object = kit.getStaticPrimitiveFieldsArray(); - } else { - object = kit.getStaticObjectFieldsArray(); - } - } else { - ValueNode handle = arguments.get(1); - object = kit.unboxHandle(handle); - } - ValueNode fieldId = arguments.get(2); - ValueNode offset = kit.getFieldOffsetFromId(fieldId); - ValueNode returnValue; - if (isSetter) { - returnValue = null; // void - ValueNode newValue = arguments.get(3); - if (fieldKind.isObject()) { - newValue = kit.unboxHandle(newValue); - } - kit.append(new RawStoreNode(object, offset, newValue, fieldKind, LocationIdentity.ANY_LOCATION)); - } else { - returnValue = kit.append(new RawLoadNode(object, offset, fieldKind, LocationIdentity.ANY_LOCATION)); - if (fieldKind.isObject()) { - returnValue = kit.boxObjectInLocalHandle(returnValue); - } - } - return returnValue; - } - - public CEntryPointData createEntryPointData() { - return CEntryPointData.create(this, CEntryPointData.DEFAULT_NAME, CEntryPointData.DEFAULT_NAME_TRANSFORMATION, "", - NoPrologue.class, AutomaticPrologueBailout.class, NoEpilogue.class, FatalExceptionHandler.class, Publish.NotPublished); - } -} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIFunctionTablesFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIFunctionTablesFeature.java index ee420f5a973b..d897e819b5e5 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIFunctionTablesFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIFunctionTablesFeature.java @@ -24,14 +24,12 @@ */ package com.oracle.svm.hosted.jni; -import java.util.ArrayList; import java.util.Arrays; import java.util.EnumSet; import java.util.List; import java.util.stream.Stream; import org.graalvm.nativeimage.AnnotationAccess; -import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.c.function.CEntryPoint; import org.graalvm.nativeimage.c.function.CFunctionPointer; import org.graalvm.nativeimage.hosted.Feature; @@ -39,7 +37,6 @@ import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; -import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.jni.CallVariant; import com.oracle.svm.core.jni.functions.JNIFunctionTables; @@ -60,15 +57,12 @@ import com.oracle.svm.hosted.c.info.StructInfo; import com.oracle.svm.hosted.code.CEntryPointCallStubSupport; import com.oracle.svm.hosted.code.CEntryPointData; -import com.oracle.svm.hosted.jni.JNIPrimitiveArrayOperationMethod.Operation; import com.oracle.svm.hosted.meta.HostedMethod; import com.oracle.svm.hosted.meta.HostedType; -import jdk.vm.ci.meta.ConstantPool; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaMethod; -import jdk.vm.ci.meta.ResolvedJavaType; /** * Prepares the initialization of the JNI function table structures at image generation time, @@ -97,8 +91,6 @@ public class JNIFunctionTablesFeature implements Feature { */ private StructInfo invokeInterfaceMetadata; - private ResolvedJavaMethod[] generatedMethods; - @Override public List> getRequiredFeatures() { return Arrays.asList(JNIAccessFeature.class); @@ -135,40 +127,6 @@ public void beforeAnalysis(BeforeAnalysisAccess arg) { return data; }); }); - - ArrayList generated = new ArrayList<>(); - MetaAccessProvider wrappedMetaAccess = metaAccess.getWrapped(); - ResolvedJavaType generatedMethodClass = wrappedMetaAccess.lookupJavaType(JNIFunctions.class); - ConstantPool constantPool = generatedMethodClass.getDeclaredMethods(false)[0].getConstantPool(); - // Generate JNI field accessors - EnumSet fldKinds = jniKinds.clone(); - fldKinds.remove(JavaKind.Void); - for (JavaKind kind : fldKinds) { - boolean[] trueFalse = {true, false}; - for (boolean isSetter : trueFalse) { - for (boolean isStatic : trueFalse) { - JNIFieldAccessorMethod method = ImageSingletons.lookup(JNIFieldAccessorMethod.Factory.class).create(kind, isSetter, isStatic, generatedMethodClass, constantPool, - wrappedMetaAccess); - AnalysisMethod analysisMethod = access.getUniverse().lookup(method); - access.getBigBang().addRootMethod(analysisMethod, true, "JNI field accessors, registered in " + JNIFunctionTablesFeature.class).registerAsEntryPoint(method.createEntryPointData()); - generated.add(method); - } - } - } - // Generate JNI primitive array operations - EnumSet primitiveArrayKinds = jniKinds.clone(); - primitiveArrayKinds.remove(JavaKind.Void); - primitiveArrayKinds.remove(JavaKind.Object); - for (JavaKind kind : primitiveArrayKinds) { - for (Operation op : Operation.values()) { - JNIPrimitiveArrayOperationMethod method = new JNIPrimitiveArrayOperationMethod(kind, op, generatedMethodClass, constantPool, wrappedMetaAccess); - AnalysisMethod analysisMethod = access.getUniverse().lookup(method); - access.getBigBang().addRootMethod(analysisMethod, true, "JNI primitive array operations, registered in " + JNIFunctionTablesFeature.class) - .registerAsEntryPoint(method.createEntryPointData()); - generated.add(method); - } - } - generatedMethods = generated.toArray(new ResolvedJavaMethod[0]); } @Override @@ -221,16 +179,6 @@ private void fillJNIFunctionsTable(CompilationAccessImpl access) { int offset = field.getOffsetInfo().getProperty(); tables.initFunctionEntry(offset, getStubFunctionPointer(access, method)); } - for (ResolvedJavaMethod accessor : generatedMethods) { - StructFieldInfo field = findFieldFor(functionTableMetadata, accessor.getName()); - - AnalysisUniverse analysisUniverse = access.getUniverse().getBigBang().getUniverse(); - AnalysisMethod analysisMethod = analysisUniverse.lookup(accessor); - HostedMethod hostedMethod = access.getUniverse().lookup(analysisMethod); - - int offset = field.getOffsetInfo().getProperty(); - tables.initFunctionEntry(offset, new MethodPointer(hostedMethod)); - } for (CallVariant variant : CallVariant.values()) { CFunctionPointer trampoline = prepareCallTrampoline(access, variant, false); String suffix = (variant == CallVariant.ARRAY) ? "A" : ((variant == CallVariant.VA_LIST) ? "V" : ""); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIGraphKit.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIGraphKit.java index 94751ed9bb86..28ba83fce049 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIGraphKit.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIGraphKit.java @@ -26,9 +26,11 @@ import com.oracle.graal.pointsto.infrastructure.ResolvedSignature; import com.oracle.graal.pointsto.meta.HostedProviders; +import com.oracle.svm.core.c.function.CEntryPointSetup.LeaveEpilogue; import com.oracle.svm.core.jni.JNIGeneratedMethodSupport; import com.oracle.svm.core.jni.access.JNIAccessibleMethod; import com.oracle.svm.core.jni.access.JNIReflectionDictionary; +import com.oracle.svm.core.jni.functions.JNIFunctions.Support.JNIEnvEnterFatalOnFailurePrologue; import com.oracle.svm.core.jni.headers.JNIMethodId; import com.oracle.svm.hosted.phases.HostedGraphKit; @@ -39,7 +41,6 @@ import jdk.graal.compiler.debug.DebugContext; import jdk.graal.compiler.nodes.CallTargetNode.InvokeKind; import jdk.graal.compiler.nodes.ConstantNode; -import jdk.graal.compiler.nodes.FixedWithNextNode; import jdk.graal.compiler.nodes.InvokeWithExceptionNode; import jdk.graal.compiler.nodes.LogicNode; import jdk.graal.compiler.nodes.NodeView; @@ -52,7 +53,6 @@ import jdk.graal.compiler.nodes.calc.ZeroExtendNode; import jdk.graal.compiler.nodes.extended.BytecodeExceptionNode; import jdk.graal.compiler.nodes.extended.GuardingNode; -import jdk.graal.compiler.nodes.java.ExceptionObjectNode; import jdk.graal.compiler.nodes.java.InstanceOfNode; import jdk.graal.compiler.util.Digest; import jdk.vm.ci.meta.JavaKind; @@ -130,65 +130,50 @@ private InvokeWithExceptionNode createStaticInvoke(String name, ValueNode... arg return createInvokeWithExceptionAndUnwind(findMethod(JNIGeneratedMethodSupport.class, name, true), InvokeKind.Static, getFrameState(), bci(), args); } - private FixedWithNextNode createStaticInvokeRetainException(String name, ValueNode... args) { - ResolvedJavaMethod method = findMethod(JNIGeneratedMethodSupport.class, name, true); - int invokeBci = bci(); - startInvokeWithException(method, InvokeKind.Static, getFrameState(), invokeBci, args); - exceptionPart(); - ExceptionObjectNode exception = exceptionObject(); - setPendingException(exception); - endInvokeWithException(); - return lastFixedNode; - } - - public InvokeWithExceptionNode nativeCallAddress(ValueNode linkage) { + public InvokeWithExceptionNode invokeNativeCallAddress(ValueNode linkage) { return createStaticInvoke("nativeCallAddress", linkage); } - public InvokeWithExceptionNode nativeCallPrologue() { + public InvokeWithExceptionNode invokeNativeCallPrologue() { return createStaticInvoke("nativeCallPrologue"); } - public InvokeWithExceptionNode nativeCallEpilogue(ValueNode handleFrame) { - return createStaticInvoke("nativeCallEpilogue", handleFrame); + public void invokeNativeCallEpilogue(ValueNode handleFrame) { + createStaticInvoke("nativeCallEpilogue", handleFrame); } - public InvokeWithExceptionNode environment() { + public InvokeWithExceptionNode invokeEnvironment() { return createStaticInvoke("environment"); } - public InvokeWithExceptionNode boxObjectInLocalHandle(ValueNode obj) { + public InvokeWithExceptionNode invokeBoxObjectInLocalHandle(ValueNode obj) { return createStaticInvoke("boxObjectInLocalHandle", obj); } - public InvokeWithExceptionNode unboxHandle(ValueNode handle) { + public InvokeWithExceptionNode invokeUnboxHandle(ValueNode handle) { return createStaticInvoke("unboxHandle", handle); } - public InvokeWithExceptionNode getFieldOffsetFromId(ValueNode fieldId) { - return createStaticInvoke("getFieldOffsetFromId", fieldId); - } - - public InvokeWithExceptionNode getNewObjectAddress(ValueNode methodId) { + public InvokeWithExceptionNode invokeGetNewObjectAddress(ValueNode methodId) { return invokeJNIMethodObjectMethod("getNewObjectAddress", methodId); } /** We trust our stored class object to be non-null. */ - public ValueNode getDeclaringClassForMethod(ValueNode methodId) { + public ValueNode invokeGetDeclaringClassForMethod(ValueNode methodId) { InvokeWithExceptionNode declaringClass = invokeJNIMethodObjectMethod("getDeclaringClassObject", methodId); return createPiNode(declaringClass, ObjectStamp.pointerNonNull(declaringClass.stamp(NodeView.DEFAULT))); } - public InvokeWithExceptionNode getJavaCallAddress(ValueNode methodId, ValueNode instance, ValueNode nonVirtual) { + public InvokeWithExceptionNode invokeGetJavaCallAddress(ValueNode methodId, ValueNode instance, ValueNode nonVirtual) { return createInvokeWithExceptionAndUnwind(findMethod(JNIAccessibleMethod.class, "getJavaCallAddress", Object.class, boolean.class), - InvokeKind.Special, getFrameState(), bci(), getUncheckedMethodObject(methodId), instance, nonVirtual); + InvokeKind.Special, getFrameState(), bci(), invokeGetUncheckedMethodObject(methodId), instance, nonVirtual); } - public InvokeWithExceptionNode getJavaCallWrapperAddressFromMethodId(ValueNode methodId) { + public InvokeWithExceptionNode invokeGetJavaCallWrapperAddressFromMethodId(ValueNode methodId) { return invokeJNIMethodObjectMethod("getCallWrapperAddress", methodId); } - public InvokeWithExceptionNode isStaticMethod(ValueNode methodId) { + public InvokeWithExceptionNode invokeIsStaticMethod(ValueNode methodId) { return invokeJNIMethodObjectMethod("isStatic", methodId); } @@ -196,51 +181,33 @@ public InvokeWithExceptionNode isStaticMethod(ValueNode methodId) { * Used in native-to-Java call wrappers where the method ID has already been used to dispatch, * and we would have crashed if something is wrong, so we can avoid null and type checks. */ - private InvokeWithExceptionNode getUncheckedMethodObject(ValueNode methodId) { + private InvokeWithExceptionNode invokeGetUncheckedMethodObject(ValueNode methodId) { return createInvokeWithExceptionAndUnwind(findMethod(JNIReflectionDictionary.class, "getMethodByID", JNIMethodId.class), InvokeKind.Static, getFrameState(), bci(), methodId); } private InvokeWithExceptionNode invokeJNIMethodObjectMethod(String name, ValueNode methodId) { - return createInvokeWithExceptionAndUnwind(findMethod(JNIAccessibleMethod.class, name), InvokeKind.Special, getFrameState(), bci(), getUncheckedMethodObject(methodId)); - } - - public InvokeWithExceptionNode getStaticPrimitiveFieldsArray() { - return createStaticInvoke("getStaticPrimitiveFieldsArray"); + return createInvokeWithExceptionAndUnwind(findMethod(JNIAccessibleMethod.class, name), InvokeKind.Special, getFrameState(), bci(), invokeGetUncheckedMethodObject(methodId)); } - public InvokeWithExceptionNode getStaticObjectFieldsArray() { - return createStaticInvoke("getStaticObjectFieldsArray"); + public void invokeSetPendingException(ValueNode obj) { + createStaticInvoke("setPendingException", obj); } - public InvokeWithExceptionNode setPendingException(ValueNode obj) { - return createStaticInvoke("setPendingException", obj); - } - - public InvokeWithExceptionNode getAndClearPendingException() { + public InvokeWithExceptionNode invokeGetAndClearPendingException() { return createStaticInvoke("getAndClearPendingException"); } - public InvokeWithExceptionNode rethrowPendingException() { - return createStaticInvoke("rethrowPendingException"); - } - - public InvokeWithExceptionNode createArrayViewAndGetAddress(ValueNode array, ValueNode isCopy) { - return createStaticInvoke("createArrayViewAndGetAddress", array, isCopy); - } - - public InvokeWithExceptionNode destroyNewestArrayViewByAddress(ValueNode address, ValueNode mode) { - return createStaticInvoke("destroyNewestArrayViewByAddress", address, mode); + public void invokeRethrowPendingException() { + createStaticInvoke("rethrowPendingException"); } - public FixedWithNextNode getPrimitiveArrayRegionRetainException(JavaKind elementKind, ValueNode array, ValueNode start, ValueNode count, ValueNode buffer) { - assert elementKind.isPrimitive(); - return createStaticInvokeRetainException("getPrimitiveArrayRegion", createObject(elementKind), array, start, count, buffer); + public void invokeJNIEnterIsolate(ValueNode env) { + createInvokeWithExceptionAndUnwind(findMethod(JNIEnvEnterFatalOnFailurePrologue.class, "enter", true), InvokeKind.Static, getFrameState(), bci(), env); } - public FixedWithNextNode setPrimitiveArrayRegionRetainException(JavaKind elementKind, ValueNode array, ValueNode start, ValueNode count, ValueNode buffer) { - assert elementKind.isPrimitive(); - return createStaticInvokeRetainException("setPrimitiveArrayRegion", createObject(elementKind), array, start, count, buffer); + public void invokeJNILeaveIsolate() { + createInvokeWithExceptionAndUnwind(findMethod(LeaveEpilogue.class, "leave", true), InvokeKind.Static, getFrameState(), bci()); } public ConstantNode createWord(long value) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIJavaCallVariantWrapperMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIJavaCallVariantWrapperMethod.java index 98b62c38ae82..c18de6e09d9a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIJavaCallVariantWrapperMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIJavaCallVariantWrapperMethod.java @@ -35,8 +35,6 @@ import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.HostedProviders; import com.oracle.svm.core.graal.code.SubstrateCallingConventionKind; -import com.oracle.svm.core.graal.nodes.CEntryPointEnterNode; -import com.oracle.svm.core.graal.nodes.CEntryPointLeaveNode; import com.oracle.svm.core.graal.nodes.CInterfaceReadNode; import com.oracle.svm.core.graal.nodes.ReadCallerStackPointerNode; import com.oracle.svm.core.graal.nodes.VaListInitializationNode; @@ -75,9 +73,9 @@ import jdk.vm.ci.meta.Signature; /** - * Generated code for taking arguments according to a specific signature and {@link CallVariant} and - * passing them on to a {@link JNIJavaCallWrapperMethod} which does the actual Java call. This - * method also enters the isolate and catches any exception. + * Generates uninterruptible code for taking arguments according to a specific signature and + * {@link CallVariant} and passing them on to a {@link JNIJavaCallWrapperMethod} which does the + * actual Java call. This method also enters the isolate and catches any exception. */ public class JNIJavaCallVariantWrapperMethod extends EntryPointCallStubMethod { @@ -156,8 +154,8 @@ public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, Hos ValueNode methodId = kit.loadLocal(slotIndex, wordKind); slotIndex += wordKind.getSlotCount(); - kit.append(CEntryPointEnterNode.enter(env)); - ValueNode callAddress = kit.getJavaCallWrapperAddressFromMethodId(methodId); + kit.invokeJNIEnterIsolate(env); + ValueNode callAddress = kit.invokeGetJavaCallWrapperAddressFromMethodId(methodId); List args = new ArrayList<>(); args.add(receiverOrClassHandle); @@ -165,7 +163,7 @@ public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, Hos args.add(kit.createInt(nonVirtual ? 1 : 0)); args.addAll(loadArguments(kit, invokeSignature, args.size(), slotIndex)); - ValueNode formerPendingException = kit.getAndClearPendingException(); + ValueNode formerPendingException = kit.invokeGetAndClearPendingException(); StampPair returnStamp = StampFactory.forDeclaredType(kit.getAssumptions(), invokeSignature.getReturnType(), false); CallTargetNode callTarget = new IndirectCallTargetNode(callAddress, args.toArray(ValueNode[]::new), returnStamp, invokeSignature.toParameterTypes(null), @@ -174,9 +172,9 @@ public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, Hos int invokeBci = kit.bci(); InvokeWithExceptionNode invoke = kit.startInvokeWithException(callTarget, kit.getFrameState(), invokeBci); kit.noExceptionPart(); - kit.setPendingException(formerPendingException); + kit.invokeSetPendingException(formerPendingException); kit.exceptionPart(); - kit.setPendingException(kit.exceptionObject()); + kit.invokeSetPendingException(kit.exceptionObject()); AbstractMergeNode invokeMerge = kit.endInvokeWithException(); ValueNode returnValue = null; @@ -193,8 +191,7 @@ public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, Hos kit.getFrameState().pop(returnKind); } - CEntryPointLeaveNode leave = new CEntryPointLeaveNode(CEntryPointLeaveNode.LeaveAction.Leave); - kit.append(leave); + kit.invokeJNILeaveIsolate(); kit.createReturn(returnValue, returnKind); return kit.finalizeGraph(); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIJavaCallWrapperMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIJavaCallWrapperMethod.java index b1a31fbc63bc..a6c40aa8ae9d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIJavaCallWrapperMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIJavaCallWrapperMethod.java @@ -68,10 +68,11 @@ import jdk.vm.ci.meta.Signature; /** - * Generated code with a specific signature for calling a Java method that has a compatible - * signature from native code. The wrapper takes care of transitioning to a Java context and back to - * native code, for catching and retaining unhandled exceptions, and if required, for unboxing - * object handle arguments and boxing an object return value. + * Generates interruptible code with a specific signature for calling a Java method that has a + * compatible signature from native code. Note that the generated interruptible code is called by a + * separately generated wrapper (see {@link JNIJavaCallVariantWrapperMethod}) that takes care of + * transitioning to a Java context and back to native code, as well as catching and retaining + * unhandled exceptions. * * @see Java 8 JNI @@ -161,7 +162,7 @@ public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, Hos JavaKind wordKind = kit.getWordTypes().getWordKind(); int slotIndex = 0; ValueNode receiverOrClassHandle = kit.loadLocal(slotIndex, wordKind); - ValueNode receiverOrClass = kit.unboxHandle(receiverOrClassHandle); + ValueNode receiverOrClass = kit.invokeUnboxHandle(receiverOrClassHandle); slotIndex += wordKind.getSlotCount(); ValueNode methodId = kit.loadLocal(slotIndex, wordKind); slotIndex += wordKind.getSlotCount(); @@ -173,19 +174,19 @@ public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, Hos JavaKind returnKind = returnValue.getStackKind(); if (returnKind.isObject()) { - returnValue = kit.boxObjectInLocalHandle(returnValue); + returnValue = kit.invokeBoxObjectInLocalHandle(returnValue); } kit.createReturn(returnValue, returnKind); return kit.finalizeGraph(); } private ValueNode createCall(JNIGraphKit kit, ResolvedSignature invokeSignature, ValueNode methodId, ValueNode receiverOrClass, ValueNode nonVirtual, ValueNode[] args) { - ValueNode declaringClass = kit.getDeclaringClassForMethod(methodId); + ValueNode declaringClass = kit.invokeGetDeclaringClassForMethod(methodId); if (!invokeSignature.getReturnKind().isObject()) { return createRegularMethodCall(kit, invokeSignature, methodId, receiverOrClass, nonVirtual, args); } - ValueNode newObjectAddress = kit.getNewObjectAddress(methodId); + ValueNode newObjectAddress = kit.invokeGetNewObjectAddress(methodId); kit.startIf(IntegerEqualsNode.create(newObjectAddress, kit.createWord(0), NodeView.DEFAULT), BranchProbabilityData.unknown()); kit.thenPart(); ValueNode methodReturnValue = createRegularMethodCall(kit, invokeSignature, methodId, receiverOrClass, nonVirtual, args); @@ -197,8 +198,8 @@ private ValueNode createCall(JNIGraphKit kit, ResolvedSignature in private static ValueNode createRegularMethodCall(JNIGraphKit kit, ResolvedSignature invokeSignature, ValueNode methodId, ValueNode receiverOrClass, ValueNode nonVirtual, ValueNode[] args) { - ValueNode methodAddress = kit.getJavaCallAddress(methodId, receiverOrClass, nonVirtual); - ValueNode isStatic = kit.isStaticMethod(methodId); + ValueNode methodAddress = kit.invokeGetJavaCallAddress(methodId, receiverOrClass, nonVirtual); + ValueNode isStatic = kit.invokeIsStaticMethod(methodId); kit.startIf(IntegerEqualsNode.create(isStatic, kit.createInt(0), NodeView.DEFAULT), BranchProbabilityData.unknown()); kit.thenPart(); ValueNode nonstaticResult = createMethodCallWithReceiver(kit, invokeSignature, methodAddress, receiverOrClass, args); @@ -232,7 +233,7 @@ protected ValueNode createNewObjectOrConstructorCall(JNIGraphKit kit, ResolvedSi protected ValueNode createConstructorCall(JNIGraphKit kit, ResolvedSignature invokeSignature, ValueNode methodId, @SuppressWarnings("unused") ValueNode declaringClass, ValueNode receiverOrClass, ValueNode[] args) { - ValueNode methodAddress = kit.getJavaCallAddress(methodId, receiverOrClass, kit.createInt(1)); + ValueNode methodAddress = kit.invokeGetJavaCallAddress(methodId, receiverOrClass, kit.createInt(1)); return createMethodCallWithReceiver(kit, invokeSignature, methodAddress, receiverOrClass, args); } @@ -297,7 +298,7 @@ private static ValueNode[] loadAndUnboxArguments(JNIGraphKit kit, ResolvedSignat } ValueNode value = kit.loadLocal(slotIndex, loadKind); if (kind.isObject()) { - value = kit.unboxHandle(value); + value = kit.invokeUnboxHandle(value); value = kit.checkObjectType(value, type, false); } else if (kind != loadKind) { value = kit.maskNumericIntBytes(value, kind); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNINativeCallWrapperMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNINativeCallWrapperMethod.java index 58b3a71ac3fe..e0244cb2bf27 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNINativeCallWrapperMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNINativeCallWrapperMethod.java @@ -108,8 +108,6 @@ public StackTraceElement asStackTraceElement(int bci) { public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, HostedProviders providers, Purpose purpose) { JNIGraphKit kit = new JNIGraphKit(debug, providers, method); - InvokeWithExceptionNode handleFrame = kit.nativeCallPrologue(); - ValueNode callAddress; if (linkage.isBuiltInFunction()) { Function createSymbol = symbolName -> CGlobalDataFeature.singleton().registerAsAccessedOrGet(CGlobalDataFactory.forSymbol(symbolName)); @@ -117,10 +115,13 @@ public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, Hos callAddress = kit.unique(new CGlobalDataLoadAddressNode(builtinAddress)); SVMImageHeapScanner.instance().rescanField(linkage, linkageBuiltInAddressField); } else { - callAddress = kit.nativeCallAddress(kit.createObject(linkage)); + callAddress = kit.invokeNativeCallAddress(kit.createObject(linkage)); } - ValueNode environment = kit.environment(); + ValueNode environment = kit.invokeEnvironment(); + + /* After the JNI prologue, we must not invoke methods that may throw an exception. */ + InvokeWithExceptionNode handleFrame = kit.invokeNativeCallPrologue(); AnalysisType javaReturnType = method.getSignature().getReturnType(); List javaArgumentTypes = method.toParameterList(); @@ -135,7 +136,8 @@ public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, Hos if (method.isStatic()) { JavaConstant clazz = kit.getConstantReflection().asJavaClass(method.getDeclaringClass()); ConstantNode clazzNode = ConstantNode.forConstant(clazz, kit.getMetaAccess(), kit.getGraph()); - ValueNode box = kit.boxObjectInLocalHandle(clazzNode); + /* Thrown exceptions may cause a memory leak, see GR-54276. */ + ValueNode box = kit.invokeBoxObjectInLocalHandle(clazzNode); jniArguments.add(box); jniArgumentTypes.add(objectHandleType); } @@ -144,7 +146,8 @@ public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, Hos AnalysisType argType = javaArgumentTypes.get(i); if (argType.getJavaKind().isObject()) { ValueNode obj = javaArguments.get(i); - arg = kit.boxObjectInLocalHandle(obj); + /* Thrown exceptions may cause a memory leak, see GR-54276. */ + arg = kit.invokeBoxObjectInLocalHandle(obj); argType = objectHandleType; } jniArguments.add(arg); @@ -156,6 +159,7 @@ public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, Hos jniReturnType = objectHandleType; } + /* Thrown exceptions may cause a memory leak, see GR-54276. */ if (getOriginal().isSynchronized()) { ValueNode monitorObject; if (method.isStatic()) { @@ -183,10 +187,14 @@ public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, Hos } if (javaReturnType.getJavaKind().isObject()) { - returnValue = kit.unboxHandle(returnValue); // before destroying handles in epilogue + /* + * Must be invoked before the handles are destroyed in the epilogue. Thrown exceptions + * may cause a memory leak, see GR-54276. + */ + returnValue = kit.invokeUnboxHandle(returnValue); } - kit.nativeCallEpilogue(handleFrame); - kit.rethrowPendingException(); + kit.invokeNativeCallEpilogue(handleFrame); + kit.invokeRethrowPendingException(); if (javaReturnType.getJavaKind().isObject()) { // Just before return to always run the epilogue and never suppress a pending exception returnValue = kit.checkObjectType(returnValue, javaReturnType, false); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIPrimitiveArrayOperationMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIPrimitiveArrayOperationMethod.java deleted file mode 100644 index 9e3cedb3e40b..000000000000 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIPrimitiveArrayOperationMethod.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright (c) 2017, 2023, 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.hosted.jni; - -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.List; - -import org.graalvm.nativeimage.c.function.CEntryPoint.FatalExceptionHandler; -import org.graalvm.nativeimage.c.function.CEntryPoint.Publish; -import org.graalvm.nativeimage.c.type.CCharPointer; -import org.graalvm.nativeimage.c.type.WordPointer; - -import com.oracle.graal.pointsto.infrastructure.ResolvedSignature; -import com.oracle.graal.pointsto.meta.AnalysisMethod; -import com.oracle.graal.pointsto.meta.HostedProviders; -import com.oracle.svm.core.c.function.CEntryPointOptions.AutomaticPrologueBailout; -import com.oracle.svm.core.c.function.CEntryPointOptions.NoEpilogue; -import com.oracle.svm.core.c.function.CEntryPointOptions.NoPrologue; -import com.oracle.svm.core.graal.nodes.CEntryPointEnterNode; -import com.oracle.svm.core.graal.nodes.CEntryPointLeaveNode; -import com.oracle.svm.core.graal.nodes.CEntryPointLeaveNode.LeaveAction; -import com.oracle.svm.core.jni.headers.JNIEnvironment; -import com.oracle.svm.core.jni.headers.JNIObjectHandle; -import com.oracle.svm.core.util.VMError; -import com.oracle.svm.hosted.code.CEntryPointData; -import com.oracle.svm.hosted.code.EntryPointCallStubMethod; - -import jdk.graal.compiler.debug.DebugContext; -import jdk.graal.compiler.nodes.FixedWithNextNode; -import jdk.graal.compiler.nodes.MergeNode; -import jdk.graal.compiler.nodes.StructuredGraph; -import jdk.graal.compiler.nodes.ValueNode; -import jdk.vm.ci.meta.ConstantPool; -import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaType; - -/** - * Generated method for operations on an array with a primitive element type via JNI. An accessor is - * specific to the {@link JavaKind element kind} of the array, and to an {@link Operation}. - * - * The generated method implements one of the following JNI Functions: - * - *
    - *
  • {@code GetBooleanArrayElements}
  • - *
  • {@code GetByteArrayElements}
  • - *
  • {@code GetCharArrayElements}
  • - *
  • {@code GetShortArrayElements}
  • - *
  • {@code GetIntArrayElements}
  • - *
  • {@code GetLongArrayElements}
  • - *
  • {@code GetFloatArrayElements}
  • - *
  • {@code GetDoubleArrayElements}
  • - *
  • {@code ReleaseBooleanArrayElements}
  • - *
  • {@code ReleaseByteArrayElements}
  • - *
  • {@code ReleaseCharArrayElements}
  • - *
  • {@code ReleaseShortArrayElements}
  • - *
  • {@code ReleaseIntArrayElements}
  • - *
  • {@code ReleaseLongArrayElements}
  • - *
  • {@code ReleaseFloatArrayElements}
  • - *
  • {@code ReleaseDoubleArrayElements}
  • - *
  • {@code GetBooleanArrayRegion}
  • - *
  • {@code GetByteArrayRegion}
  • - *
  • {@code GetCharArrayRegion}
  • - *
  • {@code GetShortArrayRegion}
  • - *
  • {@code GetIntArrayRegion}
  • - *
  • {@code GetLongArrayRegion}
  • - *
  • {@code GetFloatArrayRegion}
  • - *
  • {@code GetDoubleArrayRegion}
  • - *
  • {@code SetBooleanArrayRegion}
  • - *
  • {@code SetByteArrayRegion}
  • - *
  • {@code SetCharArrayRegion}
  • - *
  • {@code SetShortArrayRegion}
  • - *
  • {@code SetIntArrayRegion}
  • - *
  • {@code SetLongArrayRegion}
  • - *
  • {@code SetFloatArrayRegion}
  • - *
  • {@code SetDoubleArrayRegion}
  • - *
- * - * @see
JNI - * Functions - */ -public final class JNIPrimitiveArrayOperationMethod extends EntryPointCallStubMethod { - - public enum Operation { - GET_ELEMENTS, - RELEASE_ELEMENTS, - GET_REGION, - SET_REGION, - } - - private final JavaKind elementKind; - private final Operation operation; - - public JNIPrimitiveArrayOperationMethod(JavaKind elementKind, Operation operation, ResolvedJavaType declaringClass, ConstantPool constantPool, MetaAccessProvider metaAccess) { - super(createName(elementKind, operation), declaringClass, createSignature(operation, metaAccess), constantPool); - if (!EnumSet.of(JavaKind.Boolean, JavaKind.Byte, JavaKind.Char, JavaKind.Short, - JavaKind.Int, JavaKind.Long, JavaKind.Float, JavaKind.Double).contains(elementKind)) { - - throw VMError.shouldNotReachHereUnexpectedInput(elementKind); // ExcludeFromJacocoGeneratedReport - } - this.elementKind = elementKind; - this.operation = operation; - } - - private static String createName(JavaKind elementKind, Operation operation) { - StringBuilder sb = new StringBuilder(32); - String kindName = elementKind.name(); - switch (operation) { - case GET_ELEMENTS: - sb.append("Get").append(kindName).append("ArrayElements"); - break; - case RELEASE_ELEMENTS: - sb.append("Release").append(kindName).append("ArrayElements"); - break; - case GET_REGION: - sb.append("Get").append(kindName).append("ArrayRegion"); - break; - case SET_REGION: - sb.append("Set").append(kindName).append("ArrayRegion"); - break; - } - return sb.toString(); - } - - private static ResolvedSignature createSignature(Operation operation, MetaAccessProvider metaAccess) { - ResolvedJavaType objectHandleType = metaAccess.lookupJavaType(JNIObjectHandle.class); - ResolvedJavaType intType = metaAccess.lookupJavaType(int.class); - ResolvedJavaType returnType; - List args = new ArrayList<>(); - args.add(metaAccess.lookupJavaType(JNIEnvironment.class)); - args.add(objectHandleType); // jArray array; - if (operation == Operation.GET_ELEMENTS) { - args.add(metaAccess.lookupJavaType(CCharPointer.class)); // jboolean *isCopy; - returnType = metaAccess.lookupJavaType(WordPointer.class); - } else if (operation == Operation.RELEASE_ELEMENTS) { - args.add(metaAccess.lookupJavaType(WordPointer.class)); // NativeType *elems; - args.add(intType); // jint mode; - returnType = metaAccess.lookupJavaType(Void.TYPE); - } else if (operation == Operation.GET_REGION || operation == Operation.SET_REGION) { - args.add(intType); // jsize start; - args.add(intType); // jsize len; - args.add(metaAccess.lookupJavaType(WordPointer.class)); // NativeType *buf; - returnType = metaAccess.lookupJavaType(Void.TYPE); - } else { - throw VMError.shouldNotReachHereUnexpectedInput(operation); // ExcludeFromJacocoGeneratedReport - } - return ResolvedSignature.fromList(args, returnType); - } - - @Override - public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, HostedProviders providers, Purpose purpose) { - JNIGraphKit kit = new JNIGraphKit(debug, providers, method); - - List arguments = kit.getInitialArguments(); - ValueNode vmThread = arguments.get(0); - kit.append(CEntryPointEnterNode.enter(vmThread)); - - ValueNode result = null; - switch (operation) { - case GET_ELEMENTS: { - ValueNode arrayHandle = arguments.get(1); - ValueNode array = kit.unboxHandle(arrayHandle); - ValueNode isCopy = arguments.get(2); - result = kit.createArrayViewAndGetAddress(array, isCopy); - break; - } - case RELEASE_ELEMENTS: { - ValueNode address = arguments.get(2); - ValueNode mode = arguments.get(3); - kit.destroyNewestArrayViewByAddress(address, mode); - break; - } - case GET_REGION: - case SET_REGION: { - ValueNode arrayHandle = arguments.get(1); - ValueNode array = kit.unboxHandle(arrayHandle); - ValueNode start = arguments.get(2); - ValueNode count = arguments.get(3); - ValueNode buffer = arguments.get(4); - FixedWithNextNode fwn; - if (operation == Operation.GET_REGION) { - fwn = kit.getPrimitiveArrayRegionRetainException(elementKind, array, start, count, buffer); - } else { - fwn = kit.setPrimitiveArrayRegionRetainException(elementKind, array, start, count, buffer); - } - if (fwn instanceof MergeNode) { - MergeNode merge = (MergeNode) fwn; - ((MergeNode) fwn).setStateAfter(kit.getFrameState().create(kit.bci(), merge)); - } - break; - } - default: - throw VMError.shouldNotReachHereUnexpectedInput(operation); // ExcludeFromJacocoGeneratedReport - } - kit.appendStateSplitProxy(); - CEntryPointLeaveNode leave = new CEntryPointLeaveNode(LeaveAction.Leave); - kit.append(leave); - kit.createReturn(result, (result != null) ? result.getStackKind() : JavaKind.Void); - - return kit.finalizeGraph(); - } - - public CEntryPointData createEntryPointData() { - return CEntryPointData.create(this, CEntryPointData.DEFAULT_NAME, CEntryPointData.DEFAULT_NAME_TRANSFORMATION, "", - NoPrologue.class, AutomaticPrologueBailout.class, NoEpilogue.class, FatalExceptionHandler.class, Publish.NotPublished); - } -}