From 856582bedcf748d57fe74abc3d4e686162047195 Mon Sep 17 00:00:00 2001 From: Elia Trachsel Date: Wed, 12 Nov 2025 12:22:03 +0100 Subject: [PATCH 1/3] Virtualize native memory in Espresso An abstract NativeMemory class was added and linked as a field to NativeAccess. Moreover the newly added EspressoOption.nativeMemory enables selecting native memory implementations. Currently this is only available with the no-native backend since there needs to be strong synergy between Java and Native reads and writes to native memory. Implemenations of NativeMemory include: - Unsafe NativeMemory (it accesses the host memory and forwards it to the guest) - ByteArray NativeMemory (Memory gets divided into chunks and every chunk holds an addressable byte-array) - MemorySegmemt NativeMemory (Memory gets divided into chunks and every chunk holds an addressable MemorySegmemt) --- espresso/mx.espresso/suite.py | 15 + ....espresso.ffi.memory.NativeMemory$Provider | 1 + .../MemorySegmentChunkedMemoryImpl.java | 364 +++++++++++++ ...truffle.espresso.ffi.NativeAccess$Provider | 2 +- ....espresso.ffi.memory.NativeMemory$Provider | 3 + .../truffle/espresso/EspressoOptions.java | 6 + .../ffi/EspressoLibsNativeAccess.java | 63 +-- .../truffle/espresso/ffi/NativeAccess.java | 52 +- .../truffle/espresso/ffi/NoNativeAccess.java | 61 ++- .../ffi/memory/ByteArrayChunkedMemory.java | 109 ++++ .../ffi/memory/ChunkedNativeMemory.java | 341 ++++++++++++ .../espresso/ffi/memory/MemoryBuffer.java | 82 +++ .../espresso/ffi/memory/NativeMemory.java | 498 ++++++++++++++++++ .../TruffleByteArrayChunkedMemoryImpl.java | 152 ++++++ .../UnsafeByteArrayChunkedMemoryImpl.java | 152 ++++++ .../ffi/memory/UnsafeNativeMemory.java | 291 ++++++++++ .../ffi/nfi/NFIIsolatedNativeAccess.java | 159 +++--- .../espresso/ffi/nfi/NFINativeAccess.java | 63 +-- 18 files changed, 2222 insertions(+), 192 deletions(-) create mode 100644 espresso/src/com.oracle.truffle.espresso.memory.panama/src/META-INF/services/com.oracle.truffle.espresso.ffi.memory.NativeMemory$Provider create mode 100644 espresso/src/com.oracle.truffle.espresso.memory.panama/src/com/oracle/truffle/espresso/memory/panama/MemorySegmentChunkedMemoryImpl.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.ffi.memory.NativeMemory$Provider create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ByteArrayChunkedMemory.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ChunkedNativeMemory.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/MemoryBuffer.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/NativeMemory.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/TruffleByteArrayChunkedMemoryImpl.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/UnsafeByteArrayChunkedMemoryImpl.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/UnsafeNativeMemory.java diff --git a/espresso/mx.espresso/suite.py b/espresso/mx.espresso/suite.py index a76dba20f9d5..a9100f56be55 100644 --- a/espresso/mx.espresso/suite.py +++ b/espresso/mx.espresso/suite.py @@ -143,6 +143,19 @@ "checkstyle": "com.oracle.truffle.espresso", }, + "com.oracle.truffle.espresso.memory.panama": { + "subDir": "src", + "sourceDirs": ["src"], + "dependencies": [ + "com.oracle.truffle.espresso", + "truffle:TRUFFLE_API", + ], + "javaCompliance": "22+", + # GR-47124 spotbugs does not support jdk22 + "spotbugs": "false", + "checkstyle": "com.oracle.truffle.espresso", + }, + "com.oracle.truffle.espresso.hotswap": { "subDir": "src", "sourceDirs": ["src"], @@ -181,6 +194,7 @@ ], "uses": [ "com.oracle.truffle.espresso.ffi.NativeAccess.Provider", + "com.oracle.truffle.espresso.ffi.memory.NativeMemory.Provider", ], "annotationProcessors": ["truffle:TRUFFLE_DSL_PROCESSOR", "ESPRESSO_PROCESSOR"], "jacoco" : "include", @@ -553,6 +567,7 @@ "subDir": "src", "dependencies": [ "com.oracle.truffle.espresso", + "com.oracle.truffle.espresso.memory.panama", ], "distDependencies": [ "truffle:TRUFFLE_API", diff --git a/espresso/src/com.oracle.truffle.espresso.memory.panama/src/META-INF/services/com.oracle.truffle.espresso.ffi.memory.NativeMemory$Provider b/espresso/src/com.oracle.truffle.espresso.memory.panama/src/META-INF/services/com.oracle.truffle.espresso.ffi.memory.NativeMemory$Provider new file mode 100644 index 000000000000..9794f352b449 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.memory.panama/src/META-INF/services/com.oracle.truffle.espresso.ffi.memory.NativeMemory$Provider @@ -0,0 +1 @@ +com.oracle.truffle.espresso.memory.panama.MemorySegmentChunkedMemoryImpl$Provider \ No newline at end of file diff --git a/espresso/src/com.oracle.truffle.espresso.memory.panama/src/com/oracle/truffle/espresso/memory/panama/MemorySegmentChunkedMemoryImpl.java b/espresso/src/com.oracle.truffle.espresso.memory.panama/src/com/oracle/truffle/espresso/memory/panama/MemorySegmentChunkedMemoryImpl.java new file mode 100644 index 000000000000..35bb0d285631 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.memory.panama/src/com/oracle/truffle/espresso/memory/panama/MemorySegmentChunkedMemoryImpl.java @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.truffle.espresso.memory.panama; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; +import java.lang.invoke.VarHandle; +import java.nio.ByteBuffer; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.espresso.ffi.memory.ChunkedNativeMemory; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; +import com.oracle.truffle.espresso.meta.EspressoError; +import com.oracle.truffle.espresso.substitutions.Collect; + +/** + * An implementation of a chunked native memory where every chunk holds a MemorySegment. How the + * MemorySegments are allocated depends on the Arena provided in the constructor. + */ +public class MemorySegmentChunkedMemoryImpl extends ChunkedNativeMemory { + + private static final VarHandle VH_JAVA_BOOLEAN = ValueLayout.JAVA_BOOLEAN.varHandle(); + private static final VarHandle VH_JAVA_BYTE = ValueLayout.JAVA_BYTE.varHandle(); + private static final VarHandle VH_JAVA_CHAR = ValueLayout.JAVA_CHAR.varHandle(); + private static final VarHandle VH_JAVA_SHORT = ValueLayout.JAVA_SHORT.varHandle(); + private static final VarHandle VH_JAVA_INT = ValueLayout.JAVA_INT.varHandle(); + private static final VarHandle VH_JAVA_FLOAT = ValueLayout.JAVA_FLOAT.varHandle(); + private static final VarHandle VH_JAVA_DOUBLE = ValueLayout.JAVA_DOUBLE.varHandle(); + private static final VarHandle VH_JAVA_LONG = ValueLayout.JAVA_LONG.varHandle(); + + protected final Arena arena; + + protected MemorySegmentChunkedMemoryImpl(Arena arena) { + this.arena = arena; + } + + @Override + @TruffleBoundary + protected MemorySegment allocateChunk(long bytes) throws MemoryAllocationException { + // At least 8 bytes to ensure aligned accesses work. + try { + return arena.allocate(bytes, Long.BYTES); + } catch (IllegalArgumentException e) { + // We should not reach here as we control the arguments. + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere(e); + } catch (OutOfMemoryError e) { + throw new MemoryAllocationException(e); + } catch (Exception e) { + // allocation failed for some system reason + throw new MemoryAllocationException(e); + } + } + + @Override + protected long getChunkSize(MemorySegment chunk) { + return chunk.byteSize(); + } + + @Override + @TruffleBoundary(allowInlining = true) + public void putByteImpl(MemorySegment chunk, long chunkOffset, byte value, MemoryAccessMode accessMode) { + switch (accessMode) { + case MemoryAccessMode.PLAIN -> VH_JAVA_BYTE.set(chunk, chunkOffset, value); + case MemoryAccessMode.OPAQUE -> VH_JAVA_BYTE.setOpaque(chunk, chunkOffset, value); + case MemoryAccessMode.RELEASE_ACQUIRE -> VH_JAVA_BYTE.setRelease(chunk, chunkOffset, value); + case MemoryAccessMode.VOLATILE -> VH_JAVA_BYTE.setVolatile(chunk, chunkOffset, value); + } + } + + @Override + @TruffleBoundary(allowInlining = true) + public void putShortImpl(MemorySegment chunk, long chunkOffset, short value, MemoryAccessMode accessMode) { + switch (accessMode) { + case MemoryAccessMode.PLAIN -> VH_JAVA_SHORT.set(chunk, chunkOffset, value); + case MemoryAccessMode.OPAQUE -> VH_JAVA_SHORT.setOpaque(chunk, chunkOffset, value); + case MemoryAccessMode.RELEASE_ACQUIRE -> VH_JAVA_SHORT.setRelease(chunk, chunkOffset, value); + case MemoryAccessMode.VOLATILE -> VH_JAVA_SHORT.setVolatile(chunk, chunkOffset, value); + } + } + + @Override + @TruffleBoundary(allowInlining = true) + public void putIntImpl(MemorySegment chunk, long chunkOffset, int value, MemoryAccessMode accessMode) { + switch (accessMode) { + case MemoryAccessMode.PLAIN -> VH_JAVA_INT.set(chunk, chunkOffset, value); + case MemoryAccessMode.OPAQUE -> VH_JAVA_INT.setOpaque(chunk, chunkOffset, value); + case MemoryAccessMode.RELEASE_ACQUIRE -> VH_JAVA_INT.setRelease(chunk, chunkOffset, value); + case MemoryAccessMode.VOLATILE -> VH_JAVA_INT.setVolatile(chunk, chunkOffset, value); + } + } + + @Override + @TruffleBoundary(allowInlining = true) + public void putLongImpl(MemorySegment chunk, long chunkOffset, long value, MemoryAccessMode accessMode) { + switch (accessMode) { + case MemoryAccessMode.PLAIN -> VH_JAVA_LONG.set(chunk, chunkOffset, value); + case MemoryAccessMode.OPAQUE -> VH_JAVA_LONG.setOpaque(chunk, chunkOffset, value); + case MemoryAccessMode.RELEASE_ACQUIRE -> VH_JAVA_LONG.setRelease(chunk, chunkOffset, value); + case MemoryAccessMode.VOLATILE -> VH_JAVA_LONG.setVolatile(chunk, chunkOffset, value); + } + } + + @Override + @TruffleBoundary(allowInlining = true) + public byte getByteImpl(MemorySegment chunk, long chunkOffset, MemoryAccessMode accessMode) { + return switch (accessMode) { + case MemoryAccessMode.PLAIN -> (byte) VH_JAVA_BYTE.get(chunk, chunkOffset); + case MemoryAccessMode.OPAQUE -> (byte) VH_JAVA_BYTE.getOpaque(chunk, chunkOffset); + case MemoryAccessMode.RELEASE_ACQUIRE -> (byte) VH_JAVA_BYTE.getAcquire(chunk, chunkOffset); + case MemoryAccessMode.VOLATILE -> (byte) VH_JAVA_BYTE.getVolatile(chunk, chunkOffset); + }; + } + + @Override + @TruffleBoundary(allowInlining = true) + public short getShortImpl(MemorySegment chunk, long chunkOffset, MemoryAccessMode accessMode) { + return switch (accessMode) { + case MemoryAccessMode.PLAIN -> (short) VH_JAVA_SHORT.get(chunk, chunkOffset); + case MemoryAccessMode.OPAQUE -> (short) VH_JAVA_SHORT.getOpaque(chunk, chunkOffset); + case MemoryAccessMode.RELEASE_ACQUIRE -> (short) VH_JAVA_SHORT.getAcquire(chunk, chunkOffset); + case MemoryAccessMode.VOLATILE -> (short) VH_JAVA_SHORT.getVolatile(chunk, chunkOffset); + }; + } + + @Override + @TruffleBoundary(allowInlining = true) + public int getIntImpl(MemorySegment chunk, long chunkOffset, MemoryAccessMode accessMode) { + return switch (accessMode) { + case MemoryAccessMode.PLAIN -> (int) VH_JAVA_INT.get(chunk, chunkOffset); + case MemoryAccessMode.OPAQUE -> (int) VH_JAVA_INT.getOpaque(chunk, chunkOffset); + case MemoryAccessMode.RELEASE_ACQUIRE -> (int) VH_JAVA_INT.getAcquire(chunk, chunkOffset); + case MemoryAccessMode.VOLATILE -> (int) VH_JAVA_INT.getVolatile(chunk, chunkOffset); + }; + } + + @Override + @TruffleBoundary(allowInlining = true) + public long getLongImpl(MemorySegment chunk, long chunkOffset, MemoryAccessMode accessMode) { + return switch (accessMode) { + case MemoryAccessMode.PLAIN -> (long) VH_JAVA_LONG.get(chunk, chunkOffset); + case MemoryAccessMode.OPAQUE -> (long) VH_JAVA_LONG.getOpaque(chunk, chunkOffset); + case MemoryAccessMode.RELEASE_ACQUIRE -> (long) VH_JAVA_LONG.getAcquire(chunk, chunkOffset); + case MemoryAccessMode.VOLATILE -> (long) VH_JAVA_LONG.getVolatile(chunk, chunkOffset); + }; + } + + @Override + @TruffleBoundary(allowInlining = true) + public boolean compareAndSetIntImpl(MemorySegment chunk, long chunkOffset, int expected, int newValue) { + return VH_JAVA_INT.compareAndSet(chunk, chunkOffset, expected, newValue); + } + + @Override + @TruffleBoundary(allowInlining = true) + public boolean compareAndSetLongImpl(MemorySegment chunk, long chunkOffset, long expected, long newValue) { + return VH_JAVA_LONG.compareAndSet(chunk, chunkOffset, expected, newValue); + } + + @Override + public void setMemoryImpl(MemorySegment chunk, long chunkOffset, long bytes, byte value) { + chunk.asSlice(chunkOffset, bytes).fill(value); + } + + @Override + public void copyMemoryImpl(MemorySegment fromChunk, long fromOffset, MemorySegment toChunk, long toOffset, long byteSize) { + MemorySegment.copy(fromChunk, fromOffset, toChunk, toOffset, byteSize); + } + + @Override + @TruffleBoundary(allowInlining = true) + public void writeMemoryImpl(MemorySegment chunk, long chunkOffset, byte[] buf) { + MemorySegment.copy(buf, 0, chunk, ValueLayout.JAVA_BYTE, chunkOffset, buf.length); + } + + @Override + @TruffleBoundary(allowInlining = true) + public void readMemoryImpl(MemorySegment chunk, long chunkOffset, byte[] buf) { + MemorySegment.copy(chunk, ValueLayout.JAVA_BYTE, chunkOffset, buf, 0, buf.length); + } + + @Override + public ByteBuffer wrapChunk(MemorySegment chunk, long chunkOffset, int bytes) { + return chunk.asSlice(chunkOffset, bytes).asByteBuffer(); + } + + // region overwrites for default methods in NativeMemory + + @Override + @TruffleBoundary(allowInlining = true) + public void putBoolean(long address, boolean value, MemoryAccessMode accessMode) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + validateAccess(chunk.byteSize(), chunkOffset, 1); + switch (accessMode) { + case MemoryAccessMode.PLAIN -> VH_JAVA_BOOLEAN.set(chunk, chunkOffset, value); + case MemoryAccessMode.OPAQUE -> VH_JAVA_BOOLEAN.setOpaque(chunk, chunkOffset, value); + case MemoryAccessMode.RELEASE_ACQUIRE -> VH_JAVA_BOOLEAN.setRelease(chunk, chunkOffset, value); + case MemoryAccessMode.VOLATILE -> VH_JAVA_BOOLEAN.setVolatile(chunk, chunkOffset, value); + } + } + + @Override + @TruffleBoundary(allowInlining = true) + public void putChar(long address, char value, MemoryAccessMode accessMode) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + validateAccess(chunk.byteSize(), chunkOffset, Character.BYTES); + switch (accessMode) { + case MemoryAccessMode.PLAIN -> VH_JAVA_CHAR.set(chunk, chunkOffset, value); + case MemoryAccessMode.OPAQUE -> VH_JAVA_CHAR.setOpaque(chunk, chunkOffset, value); + case MemoryAccessMode.RELEASE_ACQUIRE -> VH_JAVA_CHAR.setRelease(chunk, chunkOffset, value); + case MemoryAccessMode.VOLATILE -> VH_JAVA_CHAR.setVolatile(chunk, chunkOffset, value); + } + } + + @Override + @TruffleBoundary(allowInlining = true) + public void putFloat(long address, float value, MemoryAccessMode accessMode) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + validateAccess(chunk.byteSize(), chunkOffset, Float.BYTES); + switch (accessMode) { + case MemoryAccessMode.PLAIN -> VH_JAVA_FLOAT.set(chunk, chunkOffset, value); + case MemoryAccessMode.OPAQUE -> VH_JAVA_FLOAT.setOpaque(chunk, chunkOffset, value); + case MemoryAccessMode.RELEASE_ACQUIRE -> VH_JAVA_FLOAT.setRelease(chunk, chunkOffset, value); + case MemoryAccessMode.VOLATILE -> VH_JAVA_FLOAT.setVolatile(chunk, chunkOffset, value); + } + } + + @Override + @TruffleBoundary(allowInlining = true) + public void putDouble(long address, double value, MemoryAccessMode accessMode) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + validateAccess(chunk.byteSize(), chunkOffset, Double.BYTES); + switch (accessMode) { + case MemoryAccessMode.PLAIN -> VH_JAVA_DOUBLE.set(chunk, chunkOffset, value); + case MemoryAccessMode.OPAQUE -> VH_JAVA_DOUBLE.setOpaque(chunk, chunkOffset, value); + case MemoryAccessMode.RELEASE_ACQUIRE -> VH_JAVA_DOUBLE.setRelease(chunk, chunkOffset, value); + case MemoryAccessMode.VOLATILE -> VH_JAVA_DOUBLE.setVolatile(chunk, chunkOffset, value); + } + } + + @Override + @TruffleBoundary(allowInlining = true) + public boolean getBoolean(long address, MemoryAccessMode accessMode) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + validateAccess(chunk.byteSize(), chunkOffset, 1); + return switch (accessMode) { + case MemoryAccessMode.PLAIN -> (boolean) VH_JAVA_BOOLEAN.get(chunk, chunkOffset); + case MemoryAccessMode.OPAQUE -> (boolean) VH_JAVA_BOOLEAN.getOpaque(chunk, chunkOffset); + case MemoryAccessMode.RELEASE_ACQUIRE -> (boolean) VH_JAVA_BOOLEAN.getAcquire(chunk, chunkOffset); + case MemoryAccessMode.VOLATILE -> (boolean) VH_JAVA_BOOLEAN.getVolatile(chunk, chunkOffset); + }; + } + + @Override + @TruffleBoundary(allowInlining = true) + public char getChar(long address, MemoryAccessMode accessMode) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + validateAccess(chunk.byteSize(), chunkOffset, Character.BYTES); + return switch (accessMode) { + case MemoryAccessMode.PLAIN -> (char) VH_JAVA_CHAR.get(chunk, chunkOffset); + case MemoryAccessMode.OPAQUE -> (char) VH_JAVA_CHAR.getOpaque(chunk, chunkOffset); + case MemoryAccessMode.RELEASE_ACQUIRE -> (char) VH_JAVA_CHAR.getAcquire(chunk, chunkOffset); + case MemoryAccessMode.VOLATILE -> (char) VH_JAVA_CHAR.getVolatile(chunk, chunkOffset); + }; + } + + @Override + @TruffleBoundary(allowInlining = true) + public float getFloat(long address, MemoryAccessMode accessMode) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + validateAccess(chunk.byteSize(), chunkOffset, Float.BYTES); + return switch (accessMode) { + case MemoryAccessMode.PLAIN -> (float) VH_JAVA_FLOAT.get(chunk, chunkOffset); + case MemoryAccessMode.OPAQUE -> (float) VH_JAVA_FLOAT.getOpaque(chunk, chunkOffset); + case MemoryAccessMode.RELEASE_ACQUIRE -> (float) VH_JAVA_FLOAT.getAcquire(chunk, chunkOffset); + case MemoryAccessMode.VOLATILE -> (float) VH_JAVA_FLOAT.getVolatile(chunk, chunkOffset); + }; + } + + @Override + @TruffleBoundary(allowInlining = true) + public double getDouble(long address, MemoryAccessMode accessMode) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + validateAccess(chunk.byteSize(), chunkOffset, Double.BYTES); + return switch (accessMode) { + case MemoryAccessMode.PLAIN -> (double) VH_JAVA_DOUBLE.get(chunk, chunkOffset); + case MemoryAccessMode.OPAQUE -> (double) VH_JAVA_DOUBLE.getOpaque(chunk, chunkOffset); + case MemoryAccessMode.RELEASE_ACQUIRE -> (double) VH_JAVA_DOUBLE.getAcquire(chunk, chunkOffset); + case MemoryAccessMode.VOLATILE -> (double) VH_JAVA_DOUBLE.getVolatile(chunk, chunkOffset); + }; + } + + @Override + @TruffleBoundary(allowInlining = true) + public long compareAndExchangeLong(long address, long expected, long newValue) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + validateAccess(chunk.byteSize(), chunkOffset, Long.BYTES); + return (long) VH_JAVA_LONG.compareAndExchange(chunk, chunkOffset, expected, newValue); + } + + @Override + @TruffleBoundary(allowInlining = true) + public int compareAndExchangeInt(long address, int expected, int newValue) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + validateAccess(chunk.byteSize(), chunkOffset, Integer.BYTES); + return (int) VH_JAVA_INT.compareAndExchange(chunk, chunkOffset, expected, newValue); + } + + // endregion + + @Collect(NativeMemory.class) + public static final class Provider implements NativeMemory.Provider { + + public static final String ID = "MemorySegmentChunkedMemory"; + + @Override + public String id() { + return ID; + } + + @Override + public NativeMemory create() { + // Memory Segments will be garbage collected + return new MemorySegmentChunkedMemoryImpl(Arena.ofAuto()); + } + + @Override + public boolean needsNativeAccess() { + return false; + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.ffi.NativeAccess$Provider b/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.ffi.NativeAccess$Provider index 8b154bd535b5..56d0ead75b37 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.ffi.NativeAccess$Provider +++ b/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.ffi.NativeAccess$Provider @@ -1,3 +1,3 @@ com.oracle.truffle.espresso.ffi.nfi.NFINativeAccess$Provider com.oracle.truffle.espresso.ffi.nfi.NFIIsolatedNativeAccess$Provider -com.oracle.truffle.espresso.ffi.nfi.NFISulongNativeAccess$Provider +com.oracle.truffle.espresso.ffi.nfi.NFISulongNativeAccess$Provider \ No newline at end of file diff --git a/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.ffi.memory.NativeMemory$Provider b/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.ffi.memory.NativeMemory$Provider new file mode 100644 index 000000000000..9d4f850bf25f --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/META-INF/services/com.oracle.truffle.espresso.ffi.memory.NativeMemory$Provider @@ -0,0 +1,3 @@ +com.oracle.truffle.espresso.ffi.memory.TruffleByteArrayChunkedMemoryImpl$Provider +com.oracle.truffle.espresso.ffi.memory.UnsafeByteArrayChunkedMemoryImpl$Provider +com.oracle.truffle.espresso.ffi.memory.UnsafeNativeMemory$Provider \ No newline at end of file diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoOptions.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoOptions.java index ae738fde0efc..bb1fbd67a22b 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoOptions.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoOptions.java @@ -634,6 +634,12 @@ public Long apply(String size) { usageSyntax = "") // public static final OptionKey NativeBackend = new OptionKey<>(""); + @Option(help = "NativeMemory used by Espresso. Can only be specified with no-native backend where memory can be fully virtualized.", // + category = OptionCategory.EXPERT, // + stability = OptionStability.EXPERIMENTAL, // + usageSyntax = "") // + public static final OptionKey NativeMemory = new OptionKey<>(""); + @Option(help = "Enable use of a custom Espresso implementation of boot libraries, which allows for not entering native code.\\n" + "Will be automatically enabled if there is NO native access.\\n" + "For example, this will replace the usual 'libjava'. Missing implementations will thus fail with 'UnsatifiedLinkError'.", // diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/EspressoLibsNativeAccess.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/EspressoLibsNativeAccess.java index 44d954ce1bd9..4294f9290109 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/EspressoLibsNativeAccess.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/EspressoLibsNativeAccess.java @@ -24,24 +24,19 @@ import java.nio.file.Path; -import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.TruffleLogger; import com.oracle.truffle.api.interop.ArityException; -import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.interop.UnsupportedTypeException; import com.oracle.truffle.espresso.EspressoLanguage; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; import com.oracle.truffle.espresso.impl.ContextAccessImpl; import com.oracle.truffle.espresso.libs.Lib; import com.oracle.truffle.espresso.libs.Libs; import com.oracle.truffle.espresso.libs.SubstitutionFactoryWrapper; -import com.oracle.truffle.espresso.meta.EspressoError; import com.oracle.truffle.espresso.runtime.EspressoContext; -import com.oracle.truffle.espresso.vm.UnsafeAccess; - -import sun.misc.Unsafe; /** * Uses Espresso's implementation of standard JDK libraries, falling back to a delegate if the @@ -68,11 +63,6 @@ public class EspressoLibsNativeAccess extends ContextAccessImpl implements NativeAccess { private static final TruffleLogger logger = TruffleLogger.getLogger(EspressoLanguage.ID, EspressoLibsNativeAccess.class); - /* - * Temporarily used for off-heap memory access. Will be removed with the memory virtualization - * (GR-70643) - */ - private static final Unsafe UNSAFE = UnsafeAccess.get(); private static TruffleLogger getLogger() { return logger; @@ -187,52 +177,6 @@ public SignatureCallNode createSignatureCall(NativeSignature nativeSignature) { return delegate.createSignatureCall(nativeSignature); } - @Override - public @Buffer TruffleObject allocateMemory(long size) { - // Will be removed with the memory virtualization (GR-70643) - long address = 0L; - try { - address = UNSAFE.allocateMemory(size); - } catch (OutOfMemoryError e) { - return null; - } - return TruffleByteBuffer.wrap(RawPointer.create(address), Math.toIntExact(size)); - } - - @Override - public @Buffer TruffleObject reallocateMemory(@Pointer TruffleObject buffer, long newSize) { - // Will be removed with the memory virtualization (GR-70643) - assert InteropLibrary.getUncached().isPointer(buffer); - long oldAddress = 0L; - try { - oldAddress = InteropLibrary.getUncached().asPointer(buffer); - } catch (UnsupportedMessageException e) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw EspressoError.shouldNotReachHere(e); - } - long newAddress = 0L; - try { - newAddress = UNSAFE.reallocateMemory(oldAddress, newSize); - } catch (OutOfMemoryError e) { - return null; - } - return TruffleByteBuffer.wrap(RawPointer.create(newAddress), Math.toIntExact(newSize)); - } - - @Override - public void freeMemory(@Pointer TruffleObject buffer) { - // Will be removed with the memory virtualization (GR-70643) - assert InteropLibrary.getUncached().isPointer(buffer); - long address = 0L; - try { - address = InteropLibrary.getUncached().asPointer(buffer); - } catch (UnsupportedMessageException e) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw EspressoError.shouldNotReachHere(e); - } - UNSAFE.freeMemory(address); - } - @Override public @Pointer TruffleObject createNativeClosure(TruffleObject executable, NativeSignature nativeSignature) { return delegate.createNativeClosure(executable, nativeSignature); @@ -243,6 +187,11 @@ public void prepareThread() { delegate.prepareThread(); } + @Override + public NativeMemory nativeMemory() { + return delegate.nativeMemory(); + } + @TruffleBoundary public boolean isKnownBootLibrary(String path) { Path p = Path.of(path); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/NativeAccess.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/NativeAccess.java index b3d7e3104fe7..d5c977eed874 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/NativeAccess.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/NativeAccess.java @@ -38,6 +38,7 @@ import com.oracle.truffle.api.interop.UnsupportedTypeException; import com.oracle.truffle.espresso.EspressoOptions; import com.oracle.truffle.espresso.classfile.JavaKind; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; import com.oracle.truffle.espresso.meta.EspressoError; import com.oracle.truffle.espresso.runtime.EspressoProperties; @@ -219,23 +220,48 @@ default boolean isBuiltIn(@SuppressWarnings("unused") String libname) { * {@link InteropLibrary#hasBufferElements(Object) buffer}. * @throws IllegalArgumentException if the size is negative */ - @Buffer - TruffleObject allocateMemory(long size); + default @Buffer TruffleObject allocateMemory(long size) { + long address = nativeMemory().allocateMemory(size); + if (address == 0) { + return null; + } + return RawPointer.create(address); + } /** * Similar to realloc. The result of allocating a 0-sized buffer is an implementation detail. - * + * * @return null if the memory cannot be re-allocated. Otherwise, a * {@link InteropLibrary#hasBufferElements(Object) buffer}. * @throws IllegalArgumentException if the size is negative */ - @Buffer - TruffleObject reallocateMemory(@Pointer TruffleObject buffer, long newSize); + default @Buffer TruffleObject reallocateMemory(@Pointer TruffleObject buffer, long newSize) { + long address = 0; + try { + address = InteropLibrary.getUncached().asPointer(buffer); + } catch (UnsupportedMessageException e) { + throw EspressoError.shouldNotReachHere(e); + } + long rawPointer = nativeMemory().reallocateMemory(address, newSize); + if (rawPointer == 0) { + return null; + } + return RawPointer.create(rawPointer); + } /** * Similar to free. Accessing the buffer after free may cause explosive undefined behavior. */ - void freeMemory(@Pointer TruffleObject buffer); + default void freeMemory(@Pointer TruffleObject buffer) { + long address = 0; + try { + address = InteropLibrary.getUncached().asPointer(buffer); + } catch (UnsupportedMessageException e) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere(e); + } + nativeMemory().freeMemory(address); + } /** * Sinking, make a Java method accessible to the native world. Returns an @@ -292,4 +318,18 @@ interface Provider { NativeAccess create(TruffleLanguage.Env env); } + + /** + * Retrieves the {@link NativeMemory} associated with NativeAccess. + *

+ *

Implementation notes

One should be extremely careful when determining which + * NativeMemory implementation to use for a new NativeAccess implementation. Some NativeMemory + * implementations virtualize guest memory such that addresses returned by + * {@link NativeMemory#allocateMemory(long)} do not directly map to host addresses, which can be + * problematic if the accessed "native world" works directly with those addresses as if they + * were host addresses. Thus, there needs to be synergy between Java and Native reads and writes + * to native memory. If unsure, use + * {@link com.oracle.truffle.espresso.ffi.memory.UnsafeNativeMemory}. + */ + NativeMemory nativeMemory(); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/NoNativeAccess.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/NoNativeAccess.java index 1bb6094456b0..213cb7602574 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/NoNativeAccess.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/NoNativeAccess.java @@ -23,6 +23,9 @@ package com.oracle.truffle.espresso.ffi; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.TruffleLanguage; @@ -32,13 +35,23 @@ import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.interop.UnsupportedTypeException; import com.oracle.truffle.espresso.EspressoLanguage; +import com.oracle.truffle.espresso.EspressoOptions; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; +import com.oracle.truffle.espresso.ffi.memory.TruffleByteArrayChunkedMemoryImpl; import com.oracle.truffle.espresso.meta.EspressoError; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.substitutions.Collect; public class NoNativeAccess implements NativeAccess { + private static final TruffleLogger logger = TruffleLogger.getLogger(EspressoLanguage.ID, NoNativeAccess.class); + private final NativeMemory nativeMemory; + + public NoNativeAccess(NativeMemory nativeMemory) { + this.nativeMemory = nativeMemory; + } + @Override public @Pointer TruffleObject loadLibrary(Path libraryPath) { getLogger().fine(() -> "Attempting to load library when no native access is allowed: " + libraryPath); @@ -101,23 +114,6 @@ public SignatureCallNode createSignatureCall(NativeSignature nativeSignature) { throw EspressoError.fatal("Panama not supported without native access."); } - @Override - public @Buffer TruffleObject allocateMemory(long size) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw EspressoError.fatal("AllocateMemory not supported without native access."); - } - - @Override - public @Buffer TruffleObject reallocateMemory(@Pointer TruffleObject buffer, long newSize) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw EspressoError.fatal("AllocateMemory not supported without native access."); - } - - @Override - public void freeMemory(@Pointer TruffleObject buffer) { - getLogger().warning(() -> "Attempting to free memory without native access."); - } - @Override public @Pointer TruffleObject createNativeClosure(TruffleObject executable, NativeSignature nativeSignature) { if (EspressoContext.get(null).isInitialized()) { @@ -130,6 +126,11 @@ public void freeMemory(@Pointer TruffleObject buffer) { public void prepareThread() { } + @Override + public NativeMemory nativeMemory() { + return nativeMemory; + } + private static TruffleLogger getLogger() { return logger; } @@ -146,7 +147,31 @@ public String id() { @Override public NativeAccess create(TruffleLanguage.Env env) { - return new NoNativeAccess(); + NativeMemory nativeMemory = createNativeMemory(env); + return new NoNativeAccess(nativeMemory); + } + + private static NativeMemory createNativeMemory(TruffleLanguage.Env env) { + String name = env.getOptions().get(EspressoOptions.NativeMemory); + if (name == null || name.isEmpty()) { + // default NativeMemory + return new TruffleByteArrayChunkedMemoryImpl(); + } + // try to find the user nativeMemory + List available = new ArrayList<>(); + ServiceLoader loader = ServiceLoader.load(NativeMemory.Provider.class); + boolean isNativeAccessAllowed = env.isNativeAccessAllowed(); + for (NativeMemory.Provider provider : loader) { + available.add(provider.id()); + if (provider.id().equals(name)) { + // Security check + if (!isNativeAccessAllowed && provider.needsNativeAccess()) { + throw EspressoError.fatal("Native access is disabled, but the selected NativeMemory requires it. Please choose a different NativeMemory or allow native access"); + } + return provider.create(); + } + } + throw EspressoError.fatal("Cannot find native backend: '" + name + "'. Available backends: " + available); } } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ByteArrayChunkedMemory.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ByteArrayChunkedMemory.java new file mode 100644 index 000000000000..bc49ba9dd118 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ByteArrayChunkedMemory.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.truffle.espresso.ffi.memory; + +import java.nio.ByteBuffer; +import java.util.Arrays; + +import com.oracle.truffle.espresso.libs.LibsState; + +/** + * Abstract class for shared code between classes which use a chunked byte-array native memory. + */ +public abstract class ByteArrayChunkedMemory extends ChunkedNativeMemory { + + @Override + protected byte[] allocateChunk(long bytes) throws MemoryAllocationException { + /* + * Checks if bytes fit into an int and throws a MemoryAllocationException if not. Therefore, + * it is okay to cast bytes to int without a check. + */ + if (bytes > Integer.MAX_VALUE) { + LibsState.getLogger().warning("Native memory has maximum memory allocation size Integer.MAX_VALUE! " + // + "Use MemorySegmentChunkedMemory as memory backend for larger allocation size."); + throw new MemoryAllocationException("Exceeded maximum allocation size (Integer.MAX_VALUE)"); + } + try { + return new byte[(int) bytes]; + } catch (OutOfMemoryError e) { + throw new MemoryAllocationException(e); + } + } + + @Override + protected long getChunkSize(byte[] chunk) { + return chunk.length; + } + + @Override + public void setMemoryImpl(byte[] chunk, long chunkOffset, long bytes, byte value) { + /* + * Unchecked casts are safe as the memory access has already been validated in + * ChunkedNativeMemory.setMemory + */ + assert bytes <= Integer.MAX_VALUE; + Arrays.fill(chunk, 0, (int) bytes, value); + } + + @Override + public void copyMemoryImpl(byte[] fromChunk, long fromOffset, byte[] toChunk, long toOffset, long byteSize) { + /* + * Unchecked casts are safe as the memory access has already been validated in + * ChunkedNativeMemory.copyMemory + */ + assert fromOffset <= Integer.MAX_VALUE; + assert toOffset <= Integer.MAX_VALUE; + assert byteSize <= Integer.MAX_VALUE; + System.arraycopy(fromChunk, (int) fromOffset, toChunk, (int) toOffset, (int) byteSize); + } + + @Override + public void writeMemoryImpl(byte[] chunk, long chunkOffset, byte[] buf) { + /* + * Unchecked casts are safe as the memory access has already been validated in + * ChunkedNativeMemory.writeMemory + */ + assert chunkOffset <= Integer.MAX_VALUE; + System.arraycopy(buf, 0, chunk, (int) chunkOffset, buf.length); + } + + @Override + public void readMemoryImpl(byte[] chunk, long chunkOffset, byte[] buf) { + /* + * Unchecked casts are safe as the memory access has already been validated in + * ChunkedNativeMemory.readMemory + */ + assert chunkOffset <= Integer.MAX_VALUE; + System.arraycopy(chunk, (int) chunkOffset, buf, 0, buf.length); + } + + @Override + public ByteBuffer wrapChunk(byte[] chunk, long chunkOffset, int bytes) { + /* + * Unchecked casts are safe as the memory access has already been validated in + * ChunkedNativeMemory.wrapNativeMemory + */ + assert chunkOffset <= Integer.MAX_VALUE; + return ByteBuffer.wrap(chunk, (int) chunkOffset, bytes); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ChunkedNativeMemory.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ChunkedNativeMemory.java new file mode 100644 index 000000000000..d41512507d7d --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ChunkedNativeMemory.java @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.truffle.espresso.ffi.memory; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +import com.oracle.truffle.espresso.jni.HandleStorage.HandlesStack; + +/** + * Abstract class for dividing memory into chunks, where each chunk is an object of type T. Each + * address encodes a chunk index (upper bits) and an offset within that chunk (lower bits). The + * chunk index selects an object from the {@code chunks} list; the chunk offset selects the location + * inside that chunk. + * + * @param the chunk type + */ +public abstract class ChunkedNativeMemory implements NativeMemory { + + protected static final int OFFSET_BITS = 38; // 256GB max buffer size + protected static final int CHUNK_BITS = Long.SIZE - OFFSET_BITS; // 64M chunks + protected static final long OFFSET_MASK = (1L << OFFSET_BITS) - 1; + protected static final long ALIGNMENT = Integer.BYTES; + + /* + * invariant: for all allocated addresses, 0 < chunkIndex(address) < chunks.size() + */ + protected final List chunks = new ArrayList<>(); + /* + * Should only contain ints which are valid indices into chunks. + */ + protected final HandlesStack freeChunkIndices = new HandlesStack(); + + protected abstract T allocateChunk(long bytes) throws MemoryAllocationException; + + protected ChunkedNativeMemory() { + // Sentinel block, cannot be freed. + synchronized (this) { + chunks.add(null); + } + } + + protected int getAndCheckChunkIndex(long address) throws IllegalMemoryAccessException { + int index = (int) (address >>> OFFSET_BITS); + if (!(index > 0 && index < chunks.size())) { + throw new IllegalMemoryAccessException("invalid address (chunkIndex out of bounds)!"); + } + return index; + } + + protected T getChunk(long address) throws IllegalMemoryAccessException { + int chunkIndex = getAndCheckChunkIndex(address); + return chunks.get(chunkIndex); + } + + protected long getChunkOffset(long address) { + return address & OFFSET_MASK; + } + + protected void checkOffset(long address) throws IllegalMemoryAccessException { + if (getChunkOffset(address) != 0) { + throw new IllegalMemoryAccessException("address offset must be 0"); + } + } + + protected long encodeAddress(int chunkIndex, long chunkOffset) { + assert (Long.compareUnsigned(chunkIndex, chunks.size()) < 0); + assert (Long.compareUnsigned(chunkOffset, OFFSET_MASK) <= 0); + return (((long) chunkIndex) << OFFSET_BITS) | chunkOffset; + } + + @Override + public synchronized long allocateMemory(long bytes) throws MemoryAllocationException { + if (bytes < 0) { + // We will probably not reach here but just to be safe. + throw new MemoryAllocationException("Can not allocate negative amount of bytes"); + } + /* + * We always align bytes up according to ALIGNMENT. We need to determine the maximum + * allocation size such that alignment does not cause this operation to overflow. + */ + if (bytes > (Long.MAX_VALUE & -ALIGNMENT)) { + throw new MemoryAllocationException("Can not allocate more than: " + (Long.MAX_VALUE & -ALIGNMENT)); + } + int chunkIndex = freeChunkIndices.pop(); + if (chunkIndex == -1) { + if (chunks.size() == 1 << CHUNK_BITS) { + throw new MemoryAllocationException("Out of memory (no chunk available)!"); + } + chunks.add(null); + chunkIndex = chunks.size() - 1; + } + assert chunkIndex > 0 && chunkIndex < chunks.size(); + /* + * Ensure memory is aligned so that guest code using sub-word CAS or similar operations, + * which may temporarily access memory just beyond the explicitly requested region (but + * still inside the allocated, implicitly aligned chunk), will not encounter out-of-bounds + * errors. + */ + long size = alignUp(bytes); + T chunk = allocateChunk(size); + chunks.set(chunkIndex, chunk); + return encodeAddress(chunkIndex, 0); + } + + private static long alignUp(long bytes) { + return (bytes + (ALIGNMENT - 1)) & -(ALIGNMENT); + } + + @Override + public synchronized void freeMemory(long address) throws IllegalMemoryAccessException { + if (address == 0) { + return; + } + checkOffset(address); + int chunkIndex = getAndCheckChunkIndex(address); + chunks.set(chunkIndex, null); + freeChunkIndices.push(chunkIndex); + } + + public long reallocateMemory(long address, long bytes) throws MemoryAllocationException, IllegalMemoryAccessException { + if (bytes < 0) { + // We will probably not reach here but just to be safe. + throw new MemoryAllocationException("Can not allocate negative amount of bytes"); + } + if (address == 0) { + return allocateMemory(bytes); + } + checkOffset(address); + int oldChunkIndex = getAndCheckChunkIndex(address); + if (getChunkSize(chunks.get(oldChunkIndex)) == bytes) { + return address; // no change + } + + long newAddress = allocateMemory(bytes); + long newSize = getChunkSize(getChunk(newAddress)); + copyMemory(address, newAddress, Math.min(getChunkSize(chunks.get(oldChunkIndex)), newSize)); + freeMemory(address); + return newAddress; + } + + @Override + public void putByte(long address, byte value, MemoryAccessMode accessMode) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + T chunk = getChunk(address); + validateAccess(chunk, chunkOffset, Byte.BYTES); + putByteImpl(chunk, chunkOffset, value, accessMode); + } + + @Override + public void putShort(long address, short value, MemoryAccessMode accessMode) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + T chunk = getChunk(address); + validateAccess(chunk, chunkOffset, Short.BYTES); + putShortImpl(chunk, chunkOffset, value, accessMode); + } + + @Override + public void putInt(long address, int value, MemoryAccessMode accessMode) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + T chunk = getChunk(address); + validateAccess(chunk, chunkOffset, Integer.BYTES); + putIntImpl(chunk, chunkOffset, value, accessMode); + } + + @Override + public void putLong(long address, long value, MemoryAccessMode accessMode) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + T chunk = getChunk(address); + validateAccess(chunk, chunkOffset, Long.BYTES); + putLongImpl(chunk, chunkOffset, value, accessMode); + } + + @Override + public byte getByte(long address, MemoryAccessMode accessMode) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + T chunk = getChunk(address); + validateAccess(chunk, chunkOffset, Byte.BYTES); + return getByteImpl(chunk, chunkOffset, accessMode); + } + + @Override + public short getShort(long address, MemoryAccessMode accessMode) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + T chunk = getChunk(address); + validateAccess(chunk, chunkOffset, Short.BYTES); + return getShortImpl(chunk, chunkOffset, accessMode); + } + + @Override + public int getInt(long address, MemoryAccessMode accessMode) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + T chunk = getChunk(address); + validateAccess(chunk, chunkOffset, Integer.BYTES); + return getIntImpl(chunk, chunkOffset, accessMode); + } + + @Override + public long getLong(long address, MemoryAccessMode accessMode) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + T chunk = getChunk(address); + validateAccess(chunk, chunkOffset, Long.BYTES); + return getLongImpl(chunk, chunkOffset, accessMode); + } + + @Override + public boolean compareAndSetInt(long address, int expected, int newValue) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + T chunk = getChunk(address); + validateAccess(chunk, chunkOffset, Integer.BYTES); + return compareAndSetIntImpl(chunk, chunkOffset, expected, newValue); + } + + @Override + public boolean compareAndSetLong(long address, long expected, long newValue) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + T chunk = getChunk(address); + validateAccess(chunk, chunkOffset, Long.BYTES); + return compareAndSetLongImpl(chunk, chunkOffset, expected, newValue); + } + + @Override + public void setMemory(long address, long bytes, byte value) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + T chunk = getChunk(address); + validateAccess(chunk, chunkOffset, 0); + setMemoryImpl(chunk, chunkOffset, bytes, value); + } + + @Override + public void copyMemory(long fromAddress, long toAddress, long byteSize) throws IllegalMemoryAccessException { + long fromOffset = getChunkOffset(fromAddress); + T fromChunk = getChunk(fromAddress); + validateAccess(fromChunk, fromOffset + byteSize, 0); + + long toOffset = getChunkOffset(toAddress); + T toChunk = getChunk(toAddress); + validateAccess(toChunk, toOffset + byteSize, 0); + + copyMemoryImpl(fromChunk, fromOffset, toChunk, toOffset, byteSize); + } + + @Override + public void writeMemory(long address, byte[] buf) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + T chunk = getChunk(address); + validateAccess(chunk, chunkOffset, buf.length); + writeMemoryImpl(chunk, chunkOffset, buf); + } + + @Override + public void readMemory(long address, byte[] buf) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + T chunk = getChunk(address); + validateAccess(chunk, chunkOffset, buf.length); + readMemoryImpl(chunk, chunkOffset, buf); + } + + @Override + public ByteBuffer wrapNativeMemory(long address, int bytes) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + T chunk = getChunk(address); + validateAccess(chunk, chunkOffset + bytes, 0); + return wrapChunk(chunk, chunkOffset, bytes); + } + + /** + * Validates the memory access and as a side effect enforces int lengths for byte-array chunks + * by using {@link ChunkedNativeMemory#getChunkSize(Object)}. + * + * @param chunk the chunk that is accessed. + * @param byteIndex the biggest index accessed in the chunk. + * @param accessByteSize the byte size of the access. + */ + protected void validateAccess(T chunk, long byteIndex, int accessByteSize) throws IllegalMemoryAccessException { + validateAccess(getChunkSize(chunk), byteIndex, accessByteSize); + } + + protected void validateAccess(long length, long byteIndex, int accessByteSize) throws IllegalMemoryAccessException { + if (byteIndex < 0 || byteIndex > length - accessByteSize) { + throw new IllegalMemoryAccessException("Memory access is outside the boundaries of the allocated memory region"); + } + } + + protected abstract void putByteImpl(T chunk, long chunkOffset, byte value, MemoryAccessMode accessMode); + + protected abstract void putShortImpl(T chunk, long chunkOffset, short value, MemoryAccessMode accessMode); + + protected abstract void putIntImpl(T chunk, long chunkOffset, int value, MemoryAccessMode accessMode); + + protected abstract void putLongImpl(T chunk, long chunkOffset, long value, MemoryAccessMode accessMode); + + protected abstract byte getByteImpl(T chunk, long chunkOffset, MemoryAccessMode accessMode); + + protected abstract short getShortImpl(T chunk, long chunkOffset, MemoryAccessMode accessMode); + + protected abstract int getIntImpl(T chunk, long chunkOffset, MemoryAccessMode accessMode); + + protected abstract long getLongImpl(T chunk, long chunkOffset, MemoryAccessMode accessMode); + + protected abstract boolean compareAndSetLongImpl(T chunk, long address, long expected, long newValue); + + protected abstract boolean compareAndSetIntImpl(T chunk, long address, int expected, int newValue); + + protected abstract void setMemoryImpl(T chunk, long chunkOffset, long bytes, byte value); + + protected abstract void copyMemoryImpl(T fromChunk, long fromOffset, T toChunk, long toOffset, long byteSize); + + protected abstract void writeMemoryImpl(T chunk, long chunkOffset, byte[] buf); + + protected abstract void readMemoryImpl(T chunk, long chunkOffset, byte[] buf); + + protected abstract ByteBuffer wrapChunk(T chunk, long chunkOffset, int size); + + /** + * Returns how many bytes the chunk can hold. + */ + protected abstract long getChunkSize(T chunk); + +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/MemoryBuffer.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/MemoryBuffer.java new file mode 100644 index 000000000000..51d87465631a --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/MemoryBuffer.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.truffle.espresso.ffi.memory; + +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.IllegalMemoryAccessException; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.MemoryAllocationException; + +import java.nio.ByteBuffer; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.espresso.meta.EspressoError; + +/** + * Class representing a MemoryBuffer in the context of {@link NativeMemory}. + */ +public final class MemoryBuffer implements AutoCloseable { + private final long address; + private final ByteBuffer buffer; + private final NativeMemory nativeMemory; + + /** + * see {@link NativeMemory#allocateMemoryBuffer(int)}. + */ + MemoryBuffer( + int bytes, + NativeMemory nativeMemory) throws MemoryAllocationException { + this.address = nativeMemory.allocateMemory(bytes); + this.nativeMemory = nativeMemory; + try { + this.buffer = nativeMemory.wrapNativeMemory(address, bytes); + } catch (IllegalMemoryAccessException e) { + // we should not reach here since we are in control of the parameters + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere(e); + } + } + + /** + * @return the address of the memory region corresponding to the buffer. + */ + public long address() { + return address; + } + + /** + * @return the ByteBuffer of the memory region. + */ + public ByteBuffer buffer() { + return buffer; + } + + @Override + public void close() { + try { + nativeMemory.freeMemory(address); + } catch (IllegalMemoryAccessException e) { + // we should not reach here as we are i + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere(e); + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/NativeMemory.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/NativeMemory.java new file mode 100644 index 000000000000..5980692a1767 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/NativeMemory.java @@ -0,0 +1,498 @@ +/* + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.truffle.espresso.ffi.memory; + +import java.io.Serial; +import java.nio.ByteBuffer; + +/** + * The native memory abstraction layer for Espresso. All guest off-heap memory operations in + * Espresso now flow through this NativeMemory layer. + *

+ * When you call {@link #allocateMemory(long)}, it reserves a contiguous block of memory of the + * specified size. Any location within the block should be accessible by adding an offset (between 0 + * and the size) to the base address. + *

+ *

+ * Maximum allocation size in No-Native Mode: + *

+ * In no-native mode, the default implementation ({@link TruffleByteArrayChunkedMemoryImpl}) uses + * Java byte arrays as the backing memory. As a result, each continuous memory region can only hold + * up to {@link Integer#MAX_VALUE} bytes. If you try to allocate more than that, an + * {@link MemoryAllocationException} is thrown. + *

+ * + *

+ * If you need to use memory regions larger than {@link Integer#MAX_VALUE} (up to + * {@link Long#MAX_VALUE}), you can specify "MemorySegmentChunkedMemory" as the nativeMemory + * implementation in {@link com.oracle.truffle.espresso.EspressoOptions#NativeMemory}. + *

+ *

+ * Exceptions + *

+ * All implementations throw {@link MemoryAllocationException} if an error occurs during allocation + * (e.g., out of memory, exceeding allocation limits). {@link IllegalMemoryAccessException} is + * thrown only if all memory accesses are fully bounds-checked. This is currently true for all + * subclasses of {@link ChunkedNativeMemory}. {@link UnsafeNativeMemory}, in contrast, does + * not check memory accesses and therefore does not throw + * {@link IllegalMemoryAccessException}, instead, violating memory boundaries will most likely cause + * a JVM crash (e.g., segmentation fault). + *

+ */ +public interface NativeMemory { + /** + * Reserves a contiguous block of memory of the specified size and returns the base address. Any + * location within the block should be accessible by adding an offset (between 0 and + * {@code bytes}) to the base address. + *

+ * Even if {@code bytes == 0} a valid memory address is returned. + * + * @param bytes the number of bytes to allocate + * @return the base address of the newly allocated memory block. + * @throws MemoryAllocationException if out of memory or the implementation's maximum allocation + * size is exceeded (see class level documentation). + */ + long allocateMemory(long bytes) throws MemoryAllocationException; + + /** + * Reallocates a previously allocated block of memory to a new size. The contents of the new + * block will be the same as the original block, up to the minimum of the old and new sizes. As + * specified in {@link NativeMemory#allocateMemory} the new block should be continuously + * accessible and explicitly freed. + *

+ * Even if {@code bytes == 0} a valid memory address is returned. + *

+ * Calling this method with {@code address == 0} will directly call + * {@link NativeMemory#allocateMemory(long)} + * + * @param address the address of the previous allocated block + * @param bytes the new size of the block in bytes + * @return the base address of the newly allocated memory block. + * @throws MemoryAllocationException if out of memory or the implementation's maximum allocation + * size is exceeded (see class level documentation). + * @throws IllegalMemoryAccessException if the address provided is non-zero and was not + * previously returned by {@link NativeMemory#allocateMemory(long)} memory. + */ + long reallocateMemory(long address, long bytes) throws MemoryAllocationException, IllegalMemoryAccessException; + + /** + * Frees a previously allocated block of memory. Immediately returns if {@code address == 0}. + * + * @param address the base address of an allocated block + * @throws IllegalMemoryAccessException if the address provided is non-zero and was not + * previously returned by {@link NativeMemory#allocateMemory(long)} memory. + */ + void freeMemory(long address) throws IllegalMemoryAccessException; + + /** + * Creates a ByteBuffer view of the native memory region starting at the specified address. It + * is assumed the address points to a valid, accessible memory region with at least + * {@code bytes} bytes allocated. + * + *

+ * Important Lifetime Notes: + *

    + *
  • The returned ByteBuffer is a view of existing native memory, not an + * allocation
  • + *
  • The MemoryRegion's lifetime is not tied to the returned ByteBuffer
  • + *
  • The underlying memory may be freed while the ByteBuffer is still referenced
  • + *
  • Garbage collecting the ByteBuffer does not free the underlying memory
  • + *
+ * To create a buffer backed by newly allocated memory with explicit lifetime control, use + * {@link NativeMemory#allocateMemoryBuffer(int)} instead. + * + * @param address the starting address of the native memory region + * @param bytes the size of the memory region in bytes + * @return a direct ByteBuffer view of the native memory + * @throws IllegalMemoryAccessException if the provided address was not returned by + * {@link NativeMemory#allocateMemory(long)}, or if address plus bytes exceeds the + * bounds of the allocated memory region + */ + @SuppressWarnings("unused") + ByteBuffer wrapNativeMemory(long address, int bytes) throws IllegalMemoryAccessException; + + /** + * Allocates a MemoryBuffer with the given size. The MemoryBuffer needs to freed explicitly + * using {@link MemoryBuffer#close()}. + * + * @param bytes the number of bytes to allocate + * @return a {@link MemoryBuffer} containing the ByteBuffer and the Address of the underlying + * memory region. + * @throws MemoryAllocationException if out of memory. + */ + default MemoryBuffer allocateMemoryBuffer(int bytes) throws MemoryAllocationException { + return new MemoryBuffer(bytes, this); + } + + enum MemoryAccessMode { + PLAIN, + OPAQUE, + RELEASE_ACQUIRE, + VOLATILE + } + + /** + * Writes a byte value to the specified memory address in the given {@link MemoryAccessMode}. + * + * @throws IllegalMemoryAccessException if the provided address was not returned by + * {@link NativeMemory#allocateMemory(long)}, or if the accessed byte is outside the + * boundaries of the allocated memory region. + */ + void putByte(long address, byte value, MemoryAccessMode accessMode) throws IllegalMemoryAccessException; + + /** + * Writes a short value to the specified memory address in the given {@link MemoryAccessMode}. + * + * @throws IllegalMemoryAccessException if the provided address was not returned by + * {@link NativeMemory#allocateMemory(long)}, or if the accessed bytes are outside + * the boundaries of the allocated memory region. + */ + void putShort(long address, short value, MemoryAccessMode accessMode) throws IllegalMemoryAccessException; + + /** + * Writes an int value to the specified memory address in the given {@link MemoryAccessMode}. + * + * @throws IllegalMemoryAccessException if the provided address was not returned by + * {@link NativeMemory#allocateMemory(long)}, or if the accessed bytes are outside + * the boundaries of the allocated memory region. + */ + void putInt(long address, int value, MemoryAccessMode accessMode) throws IllegalMemoryAccessException; + + /** + * Writes a long value to the specified memory address in the given {@link MemoryAccessMode}. + * + * @throws IllegalMemoryAccessException if the provided address was not returned by + * {@link NativeMemory#allocateMemory(long)}, or if the accessed bytes are outside + * the boundaries of the allocated memory region. + */ + void putLong(long address, long value, MemoryAccessMode accessMode) throws IllegalMemoryAccessException; + + /** + * Writes a boolean value to the specified memory address in the given {@link MemoryAccessMode}. + * + * @throws IllegalMemoryAccessException if the provided address was not returned by + * {@link NativeMemory#allocateMemory(long)}, or if the accessed byte is outside the + * boundaries of the allocated memory region. + */ + default void putBoolean(long address, boolean value, MemoryAccessMode accessMode) throws IllegalMemoryAccessException { + putByte(address, value ? (byte) 1 : (byte) 0, accessMode); + } + + /** + * Writes a char value to the specified memory address in the given {@link MemoryAccessMode}. + * + * @throws IllegalMemoryAccessException if the provided address was not returned by + * {@link NativeMemory#allocateMemory(long)}, or if the accessed bytes are outside + * the boundaries of the allocated memory region. + */ + default void putChar(long address, char value, MemoryAccessMode accessMode) throws IllegalMemoryAccessException { + putShort(address, (short) value, accessMode); + } + + /** + * Writes a float value to the specified memory address in the given {@link MemoryAccessMode}. + * + * @throws IllegalMemoryAccessException if the provided address was not returned by + * {@link NativeMemory#allocateMemory(long)}, or if the accessed bytes are outside + * the boundaries of the allocated memory region. + */ + default void putFloat(long address, float value, MemoryAccessMode accessMode) throws IllegalMemoryAccessException { + putInt(address, Float.floatToRawIntBits(value), accessMode); + } + + /** + * Writes a byte double to the specified memory address in the given {@link MemoryAccessMode}. + * + * @throws IllegalMemoryAccessException if the provided address was not returned by + * {@link NativeMemory#allocateMemory(long)}, or if the accessed bytes are outside + * the boundaries of the allocated memory region. + */ + default void putDouble(long address, double value, MemoryAccessMode accessMode) throws IllegalMemoryAccessException { + putLong(address, Double.doubleToRawLongBits(value), accessMode); + } + + /** + * Reads a byte value from the specified memory address in the given {@link MemoryAccessMode}. + * + * @throws IllegalMemoryAccessException if the provided address was not returned by + * {@link NativeMemory#allocateMemory(long)}, or if the accessed byte is outside the + * boundaries of the allocated memory region. + */ + byte getByte(long address, MemoryAccessMode accessMode) throws IllegalMemoryAccessException; + + /** + * Reads a short value from the specified memory address in the given {@link MemoryAccessMode}. + * + * @throws IllegalMemoryAccessException if the provided address was not returned by + * {@link NativeMemory#allocateMemory(long)}, or if the accessed bytes are outside + * the boundaries of the allocated memory region. + */ + short getShort(long address, MemoryAccessMode accessMode) throws IllegalMemoryAccessException; + + /** + * Reads an int value from the specified memory address in the given {@link MemoryAccessMode}. + * + * @throws IllegalMemoryAccessException if the provided address was not returned by + * {@link NativeMemory#allocateMemory(long)}, or if the accessed bytes are outside + * the boundaries of the allocated memory region. + */ + int getInt(long address, MemoryAccessMode accessMode) throws IllegalMemoryAccessException; + + /** + * Reads a long value from the specified memory address in the given {@link MemoryAccessMode}. + * + * @throws IllegalMemoryAccessException if the provided address was not returned by + * {@link NativeMemory#allocateMemory(long)}, or if the accessed bytes are outside + * the boundaries of the allocated memory region. + */ + long getLong(long address, MemoryAccessMode accessMode) throws IllegalMemoryAccessException; + + /** + * Reads a boolean value from the specified memory address in the given + * {@link MemoryAccessMode}. + * + * @throws IllegalMemoryAccessException if the provided address was not returned by + * {@link NativeMemory#allocateMemory(long)}, or if the accessed byte is outside the + * boundaries of the allocated memory region. + */ + default boolean getBoolean(long address, MemoryAccessMode accessMode) throws IllegalMemoryAccessException { + return getByte(address, accessMode) != 0; + } + + /** + * Reads a char value from the specified memory address in the given {@link MemoryAccessMode}. + * + * @throws IllegalMemoryAccessException if the provided address was not returned by + * {@link NativeMemory#allocateMemory(long)}, or if the accessed bytes are outside + * the boundaries of the allocated memory region. + */ + default char getChar(long address, MemoryAccessMode accessMode) throws IllegalMemoryAccessException { + return (char) getShort(address, accessMode); + } + + /** + * Reads a float value from the specified memory address in the given {@link MemoryAccessMode}. + * + * @throws IllegalMemoryAccessException if the provided address was not returned by + * {@link NativeMemory#allocateMemory(long)}, or if the accessed bytes are outside + * the boundaries of the allocated memory region. + */ + default float getFloat(long address, MemoryAccessMode accessMode) throws IllegalMemoryAccessException { + return Float.intBitsToFloat(getInt(address, accessMode)); + } + + /** + * Reads a double value from the specified memory address in the given {@link MemoryAccessMode}. + * + * @throws IllegalMemoryAccessException if the provided address was not returned by + * {@link NativeMemory#allocateMemory(long)}, or if the accessed bytes are outside + * the boundaries of the allocated memory region. + */ + default double getDouble(long address, MemoryAccessMode accessMode) throws IllegalMemoryAccessException { + return Double.longBitsToDouble(getLong(address, accessMode)); + } + + /** + * Atomically sets a long value at the specified address if its current value equals the + * expected value. + * + * @return true if the value was updated, false otherwise. + * @throws IllegalMemoryAccessException if the provided address was not returned by + * {@link NativeMemory#allocateMemory(long)}, or if the accessed bytes are outside + * the boundaries of the allocated memory region. + */ + boolean compareAndSetLong(long address, long expected, long newValue) throws IllegalMemoryAccessException; + + /** + * Atomically sets an int value at the specified address if its current value equals the + * expected value. + * + * @return true if the value was updated, false otherwise. + * @throws IllegalMemoryAccessException if the provided address was not returned by + * {@link NativeMemory#allocateMemory(long)}, or if the accessed bytes are outside + * the boundaries of the allocated memory region. + */ + boolean compareAndSetInt(long address, int expected, int newValue) throws IllegalMemoryAccessException; + + /** + * Atomically sets a long value at the specified address if its current value equals the + * expected value. + * + * @return the value read at the given address. + * @throws IllegalMemoryAccessException if the provided address was not returned by + * {@link NativeMemory#allocateMemory(long)}, or if the accessed bytes are outside + * the boundaries of the allocated memory region. + */ + default long compareAndExchangeLong(long address, long expected, long newValue) throws IllegalMemoryAccessException { + long previous; + do { + previous = getLong(address, MemoryAccessMode.VOLATILE); + if (previous != expected) { + return previous; + } + } while (!compareAndSetLong(address, expected, newValue)); + return previous; + } + + /** + * Atomically sets an int value at the specified address if its current value equals the + * expected value. + * + * @return the value read at the given address. + * @throws IllegalMemoryAccessException if the provided address was not returned by + * {@link NativeMemory#allocateMemory(long)}, or if the accessed bytes are outside + * the boundaries of the allocated memory region. + */ + default int compareAndExchangeInt(long address, int expected, int newValue) throws IllegalMemoryAccessException { + int previous; + do { + previous = getInt(address, MemoryAccessMode.VOLATILE); + if (previous != expected) { + return previous; + } + } while (!compareAndSetInt(address, expected, newValue)); + return previous; + } + + /** + * Sets {@code bytes} bytes in the memory region starting at {@code address} to the specified + * {@code value}. + * + * @param address the base address of the memory region to set + * @param bytes the number of bytes to set to the given value + * @param value the byte value to write throughout the specified region + * @throws IllegalMemoryAccessException if the provided address was not returned by + * {@link NativeMemory#allocateMemory(long)}, or if the accessed bytes are outside + * the boundaries of the allocated memory region. + */ + void setMemory(long address, long bytes, byte value) throws IllegalMemoryAccessException; + + /** + * Copies bytes-many bytes from the source address to the destination address. This method works + * in plain accessMode. + * + * @param srcBase The source memory address. + * @param destBase The destination memory address. + * @param bytes Number of bytes copied. + * @throws IllegalMemoryAccessException if the provided address was not returned by + * {@link NativeMemory#allocateMemory(long)}, or if the accessed bytes are outside + * the boundaries of the allocated memory region. + */ + default void copyMemory(long srcBase, + long destBase, + long bytes) throws IllegalMemoryAccessException { + if (srcBase < destBase && srcBase + bytes > destBase) { + // Overlap detected --> copy from end to beginning + for (long offset = bytes - 1; offset >= 0; offset--) { + putByte(destBase + offset, getByte(srcBase + offset, MemoryAccessMode.PLAIN), MemoryAccessMode.PLAIN); + } + } else { + // No overlap or dest is before src --> copy normally + for (long offset = 0; offset < bytes; offset++) { + putByte(destBase + offset, getByte(srcBase + offset, MemoryAccessMode.PLAIN), MemoryAccessMode.PLAIN); + } + } + } + + /** + * Reads bytes from the memory address into the given buffer until its completely filled. This + * method works in plain accessMode. + * + * @param addr Memory address where we read bytes from. + * @param buf The buffer where we write the bytes to. + * @throws IllegalMemoryAccessException if the provided address was not returned by + * {@link NativeMemory#allocateMemory(long)}, or if the accessed bytes are outside + * the boundaries of the allocated memory region. + */ + default void readMemory(long addr, byte[] buf) throws IllegalMemoryAccessException { + for (int offset = 0; offset < buf.length; offset++) { + buf[offset] = getByte(addr + offset, MemoryAccessMode.PLAIN); + } + } + + /** + * Writes all bytes from the given buffer to the memory address. This method works in plain + * accessMode. + * + * @param addr Memory address where we write bytes from. + * @param buf The buffer where we read the bytes into. + * @throws IllegalMemoryAccessException if the provided address was not returned by + * {@link NativeMemory#allocateMemory(long)}, or if the accessed bytes are outside + * the boundaries of the allocated memory region. + */ + default void writeMemory(long addr, byte[] buf) throws IllegalMemoryAccessException { + for (int offset = 0; offset < buf.length; offset++) { + putByte(addr + offset, buf[offset], MemoryAccessMode.PLAIN); + } + } + + interface Provider { + /** + * @return the name of the nativeMemory + */ + String id(); + + /** + * @return an instance of the nativeMemory + */ + NativeMemory create(); + + /** + * @return whether the underlying NativeMemory needs native access. This should return false + * iff. the implementation is safe to use when native access is not allowed + * ({@code env.isNativeAccessAllowed() == false}). + */ + boolean needsNativeAccess(); + } + + /** + * Exception class for illegal memory accesses. + *

+ * When accessing memory, you must always use an address returned by + * {@link NativeMemory#allocateMemory(long)} and a valid offset. An offset is valid if it does + * not exceed the boundaries of the allocated memory region. If these constraints are violated + * this exception is thrown. + */ + class IllegalMemoryAccessException extends Exception { + @Serial private static final long serialVersionUID = 1L; + + public IllegalMemoryAccessException(String message) { + super(message); + } + } + + /** + * Exception class for failures when allocating memory. + */ + class MemoryAllocationException extends Exception { + @Serial private static final long serialVersionUID = 1L; + + public MemoryAllocationException(String message) { + super(message); + } + + public MemoryAllocationException(Throwable cause) { + super("Could not allocate memory: ", cause); + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/TruffleByteArrayChunkedMemoryImpl.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/TruffleByteArrayChunkedMemoryImpl.java new file mode 100644 index 000000000000..acd28ba83587 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/TruffleByteArrayChunkedMemoryImpl.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.truffle.espresso.ffi.memory; + +import java.nio.ByteOrder; + +import com.oracle.truffle.api.memory.ByteArraySupport; +import com.oracle.truffle.espresso.substitutions.Collect; + +/** + * An implementation of a chunked byte-array native memory. It uses Truffle's + * {@link ByteArraySupport} for accessing the byte arrays. + */ +public class TruffleByteArrayChunkedMemoryImpl extends ByteArrayChunkedMemory { + + private static final ByteArraySupport BYTES = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN + ? ByteArraySupport.littleEndian() + : ByteArraySupport.bigEndian(); + + @Override + public void putByteImpl(byte[] chunk, long chunkOffset, byte value, MemoryAccessMode accessMode) { + switch (accessMode) { + case PLAIN -> BYTES.putByte(chunk, chunkOffset, value); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> BYTES.putByteVolatile(chunk, chunkOffset, value); + } + } + + @Override + public void putShortImpl(byte[] chunk, long chunkOffset, short value, MemoryAccessMode accessMode) { + switch (accessMode) { + case PLAIN -> BYTES.putShort(chunk, chunkOffset, value); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> BYTES.putShortVolatile(chunk, chunkOffset, value); + } + } + + @Override + public void putIntImpl(byte[] chunk, long chunkOffset, int value, MemoryAccessMode accessMode) { + switch (accessMode) { + case PLAIN -> BYTES.putInt(chunk, chunkOffset, value); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> BYTES.putIntVolatile(chunk, chunkOffset, value); + } + } + + @Override + public void putLongImpl(byte[] chunk, long chunkOffset, long value, MemoryAccessMode accessMode) { + switch (accessMode) { + case PLAIN -> BYTES.putLong(chunk, chunkOffset, value); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> BYTES.putLongVolatile(chunk, chunkOffset, value); + } + } + + @Override + public byte getByteImpl(byte[] chunk, long chunkOffset, MemoryAccessMode accessMode) { + return switch (accessMode) { + case PLAIN -> BYTES.getByte(chunk, chunkOffset); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> BYTES.getByteVolatile(chunk, chunkOffset); + }; + } + + @Override + public short getShortImpl(byte[] chunk, long chunkOffset, MemoryAccessMode accessMode) { + return switch (accessMode) { + case PLAIN -> BYTES.getShort(chunk, chunkOffset); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> BYTES.getShortVolatile(chunk, chunkOffset); + }; + } + + @Override + public int getIntImpl(byte[] chunk, long chunkOffset, MemoryAccessMode accessMode) { + return switch (accessMode) { + case PLAIN -> BYTES.getInt(chunk, chunkOffset); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> BYTES.getIntVolatile(chunk, chunkOffset); + }; + } + + @Override + public long getLongImpl(byte[] chunk, long chunkOffset, MemoryAccessMode accessMode) { + return switch (accessMode) { + case PLAIN -> BYTES.getLong(chunk, chunkOffset); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> BYTES.getLongVolatile(chunk, chunkOffset); + }; + } + + @Override + public boolean compareAndSetIntImpl(byte[] chunk, long chunkOffset, int expected, int newValue) { + return BYTES.compareAndExchangeInt(chunk, chunkOffset, expected, newValue) == expected; + } + + @Override + public boolean compareAndSetLongImpl(byte[] chunk, long chunkOffset, long expected, long newValue) { + return BYTES.compareAndExchangeLong(chunk, chunkOffset, expected, newValue) == expected; + } + + // Override of default method in NativeMemory for efficiency + @Override + public int compareAndExchangeInt(long address, int expected, int newValue) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk, chunkOffset, Integer.BYTES); + return BYTES.compareAndExchangeInt(chunk, chunkOffset, expected, newValue); + } + + // Override of default method in NativeMemory for efficiency + @Override + public long compareAndExchangeLong(long address, long expected, long newValue) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk, chunkOffset, Long.BYTES); + return BYTES.compareAndExchangeLong(chunk, chunkOffset, expected, newValue); + } + + @Collect(NativeMemory.class) + public static final class Provider implements NativeMemory.Provider { + + public static final String ID = "TruffleByteArrayChunkedMemory"; + + @Override + public String id() { + return ID; + } + + @Override + public NativeMemory create() { + return new TruffleByteArrayChunkedMemoryImpl(); + } + + @Override + public boolean needsNativeAccess() { + return false; + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/UnsafeByteArrayChunkedMemoryImpl.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/UnsafeByteArrayChunkedMemoryImpl.java new file mode 100644 index 000000000000..34a01ff51bab --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/UnsafeByteArrayChunkedMemoryImpl.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.truffle.espresso.ffi.memory; + +import com.oracle.truffle.espresso.substitutions.Collect; +import com.oracle.truffle.espresso.vm.UnsafeAccess; + +import sun.misc.Unsafe; + +/** + * An implementation of a chunked byte-array native memory. It uses Unsafe for accessing the byte + * arrays. + */ +public class UnsafeByteArrayChunkedMemoryImpl extends ByteArrayChunkedMemory { + + private static final Unsafe UNSAFE = UnsafeAccess.get(); + + @Override + public void putByteImpl(byte[] chunk, long chunkOffset, byte value, MemoryAccessMode accessMode) { + switch (accessMode) { + case PLAIN -> UNSAFE.putByte(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset, value); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> + UNSAFE.putByteVolatile(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset, value); + } + } + + @Override + public void putShortImpl(byte[] chunk, long chunkOffset, short value, MemoryAccessMode accessMode) { + switch (accessMode) { + case PLAIN -> UNSAFE.putShort(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset, value); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> + UNSAFE.putShortVolatile(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset, value); + } + } + + @Override + public void putIntImpl(byte[] chunk, long chunkOffset, int value, MemoryAccessMode accessMode) { + switch (accessMode) { + case PLAIN -> UNSAFE.putInt(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset, value); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> + UNSAFE.putIntVolatile(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset, value); + } + } + + @Override + public void putLongImpl(byte[] chunk, long chunkOffset, long value, MemoryAccessMode accessMode) { + switch (accessMode) { + case PLAIN -> UNSAFE.putLong(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset, value); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> + UNSAFE.putLongVolatile(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset, value); + } + } + + @Override + public byte getByteImpl(byte[] chunk, long chunkOffset, MemoryAccessMode accessMode) { + return switch (accessMode) { + case PLAIN -> UNSAFE.getByte(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> + UNSAFE.getByteVolatile(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset); + }; + } + + @Override + public short getShortImpl(byte[] chunk, long chunkOffset, MemoryAccessMode accessMode) { + return switch (accessMode) { + case PLAIN -> UNSAFE.getShort(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> + UNSAFE.getShortVolatile(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset); + }; + } + + @Override + public int getIntImpl(byte[] chunk, long chunkOffset, MemoryAccessMode accessMode) { + return switch (accessMode) { + case PLAIN -> UNSAFE.getInt(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> + UNSAFE.getIntVolatile(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset); + }; + } + + @Override + public long getLongImpl(byte[] chunk, long chunkOffset, MemoryAccessMode accessMode) { + return switch (accessMode) { + case PLAIN -> UNSAFE.getLong(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> + UNSAFE.getLongVolatile(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset); + }; + } + + @Override + public double getDouble(long address, MemoryAccessMode accessMode) throws IllegalMemoryAccessException { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(chunkOffset), Double.BYTES); + return switch (accessMode) { + case PLAIN -> UNSAFE.getDouble(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> + UNSAFE.getDoubleVolatile(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset); + }; + } + + @Override + public boolean compareAndSetLongImpl(byte[] chunk, long chunkOffset, long expected, long newValue) { + return UNSAFE.compareAndSwapLong(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset, expected, newValue); + } + + @Override + public boolean compareAndSetIntImpl(byte[] chunk, long chunkOffset, int expected, int newValue) { + return UNSAFE.compareAndSwapInt(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset, expected, newValue); + } + + @Collect(NativeMemory.class) + public static final class Provider implements NativeMemory.Provider { + + public static final String ID = "UnsafeByteArrayChunkedMemory"; + + @Override + public String id() { + return ID; + } + + @Override + public NativeMemory create() { + return new UnsafeByteArrayChunkedMemoryImpl(); + } + + @Override + public boolean needsNativeAccess() { + return false; + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/UnsafeNativeMemory.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/UnsafeNativeMemory.java new file mode 100644 index 000000000000..70070ed69e37 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/UnsafeNativeMemory.java @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.truffle.espresso.ffi.memory; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import com.oracle.truffle.espresso.meta.EspressoError; +import com.oracle.truffle.espresso.substitutions.Collect; +import com.oracle.truffle.espresso.vm.UnsafeAccess; + +import sun.misc.Unsafe; + +/** + * UnsafeNativeMemory which accesses the host memory via Unsafe and exposes it to the guest. + */ +public class UnsafeNativeMemory implements NativeMemory { + + private static final Unsafe UNSAFE = UnsafeAccess.get(); + private static final Class DIRECT_BYTE_BUFFER_CLASS; + private static final long ADDRESS_FIELD_OFFSET; + private static final long CAPACITY_FIELD_OFFSET; + + @SuppressWarnings("deprecation") + private static long getBufferFieldOffset(String name) throws NoSuchFieldException { + // TODO replace with panama? + return UNSAFE.objectFieldOffset(java.nio.Buffer.class.getDeclaredField(name)); + } + + static { + try { + ADDRESS_FIELD_OFFSET = getBufferFieldOffset("address"); + CAPACITY_FIELD_OFFSET = getBufferFieldOffset("capacity"); + DIRECT_BYTE_BUFFER_CLASS = Class.forName("java.nio.DirectByteBuffer"); + } catch (ClassNotFoundException | NoSuchFieldException e) { + throw EspressoError.shouldNotReachHere(e); + } + } + + @Override + public long allocateMemory(long bytes) throws MemoryAllocationException { + long size = bytes == 0 ? 1 : bytes; + try { + return UNSAFE.allocateMemory(size); + } catch (OutOfMemoryError | RuntimeException e) { + throw new MemoryAllocationException(e); + } + } + + @Override + public long reallocateMemory(long address, long bytes) throws MemoryAllocationException { + long size = bytes == 0 ? 1 : bytes; + try { + return UNSAFE.reallocateMemory(address, size); + } catch (OutOfMemoryError | RuntimeException e) { + throw new MemoryAllocationException(e); + } + } + + @Override + public void freeMemory(long address) { + UNSAFE.freeMemory(address); + } + + @Override + public void setMemory(long address, long bytes, byte value) { + UNSAFE.setMemory(address, bytes, value); + } + + @Override + public void putBoolean(long address, boolean value, MemoryAccessMode accessMode) { + switch (accessMode) { + case PLAIN -> UNSAFE.putBoolean(null, address, value); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> UNSAFE.putBooleanVolatile(null, address, value); + } + } + + @Override + public void putByte(long address, byte value, MemoryAccessMode accessMode) { + switch (accessMode) { + case PLAIN -> UNSAFE.putByte(null, address, value); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> UNSAFE.putByteVolatile(null, address, value); + } + } + + @Override + public void putChar(long address, char value, MemoryAccessMode accessMode) { + switch (accessMode) { + case PLAIN -> UNSAFE.putChar(null, address, value); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> UNSAFE.putCharVolatile(null, address, value); + } + } + + @Override + public void putShort(long address, short value, MemoryAccessMode accessMode) { + switch (accessMode) { + case PLAIN -> UNSAFE.putShort(null, address, value); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> UNSAFE.putShortVolatile(null, address, value); + } + } + + @Override + public void putInt(long address, int value, MemoryAccessMode accessMode) { + switch (accessMode) { + case PLAIN -> UNSAFE.putInt(null, address, value); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> UNSAFE.putIntVolatile(null, address, value); + } + } + + @Override + public void putFloat(long address, float value, MemoryAccessMode accessMode) { + switch (accessMode) { + case PLAIN -> UNSAFE.putFloat(null, address, value); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> UNSAFE.putFloatVolatile(null, address, value); + } + } + + @Override + public void putDouble(long address, double value, MemoryAccessMode accessMode) { + switch (accessMode) { + case PLAIN -> UNSAFE.putDouble(null, address, value); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> UNSAFE.putDoubleVolatile(null, address, value); + } + } + + @Override + public void putLong(long address, long value, MemoryAccessMode accessMode) { + switch (accessMode) { + case PLAIN -> UNSAFE.putLong(null, address, value); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> UNSAFE.putLongVolatile(null, address, value); + } + } + + @Override + public boolean getBoolean(long address, MemoryAccessMode accessMode) { + return switch (accessMode) { + case PLAIN -> UNSAFE.getBoolean(null, address); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> UNSAFE.getBooleanVolatile(null, address); + }; + } + + @Override + public byte getByte(long address, MemoryAccessMode accessMode) { + return switch (accessMode) { + case PLAIN -> UNSAFE.getByte(null, address); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> UNSAFE.getByteVolatile(null, address); + }; + } + + @Override + public char getChar(long address, MemoryAccessMode accessMode) { + return switch (accessMode) { + case PLAIN -> UNSAFE.getChar(null, address); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> UNSAFE.getCharVolatile(null, address); + }; + } + + @Override + public short getShort(long address, MemoryAccessMode accessMode) { + return switch (accessMode) { + case PLAIN -> UNSAFE.getShort(null, address); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> UNSAFE.getShortVolatile(null, address); + }; + } + + @Override + public int getInt(long address, MemoryAccessMode accessMode) { + return switch (accessMode) { + case PLAIN -> UNSAFE.getInt(null, address); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> UNSAFE.getIntVolatile(null, address); + }; + } + + @Override + public float getFloat(long address, MemoryAccessMode accessMode) { + return switch (accessMode) { + case PLAIN -> UNSAFE.getFloat(null, address); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> UNSAFE.getFloatVolatile(null, address); + }; + } + + @Override + public double getDouble(long address, MemoryAccessMode accessMode) { + return switch (accessMode) { + case PLAIN -> UNSAFE.getDouble(null, address); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> UNSAFE.getDoubleVolatile(null, address); + }; + } + + @Override + public long getLong(long address, MemoryAccessMode accessMode) { + return switch (accessMode) { + case PLAIN -> UNSAFE.getLong(null, address); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> UNSAFE.getLongVolatile(null, address); + }; + } + + @Override + public boolean compareAndSetInt(long address, int expected, int newValue) { + return UNSAFE.compareAndSwapInt(null, address, expected, newValue); + } + + @Override + public boolean compareAndSetLong(long address, long expected, long newValue) { + return UNSAFE.compareAndSwapLong(null, address, expected, newValue); + } + + @Override + public void copyMemory(long fromAddress, long toAddress, long byteSize) { + UNSAFE.copyMemory(fromAddress, toAddress, byteSize); + } + + @Override + public void writeMemory(long address, byte[] buf) { + long srcOffset = UNSAFE.arrayBaseOffset(byte[].class); + UNSAFE.copyMemory( + buf, + srcOffset, + null, + address, + buf.length); + } + + @Override + public void readMemory(long address, byte[] buf) { + long dstOffset = UNSAFE.arrayBaseOffset(byte[].class); + UNSAFE.copyMemory( + null, + address, + buf, + dstOffset, + buf.length); + } + + @Override + public ByteBuffer wrapNativeMemory(long address, int bytes) { + int capacity = Math.toIntExact(bytes); + ByteBuffer buffer = null; + try { + buffer = (ByteBuffer) UNSAFE.allocateInstance(DIRECT_BYTE_BUFFER_CLASS); + } catch (InstantiationException e) { + throw EspressoError.shouldNotReachHere(e); + } + UNSAFE.putLong(buffer, ADDRESS_FIELD_OFFSET, address); + UNSAFE.putInt(buffer, CAPACITY_FIELD_OFFSET, capacity); + buffer.clear(); + buffer.order(ByteOrder.nativeOrder()); + return buffer; + } + + @Collect(NativeMemory.class) + public static final class Provider implements NativeMemory.Provider { + + public static final String ID = "UnsafeNativeMemory"; + + @Override + public String id() { + return ID; + } + + @Override + public NativeMemory create() { + return new UnsafeNativeMemory(); + } + + @Override + public boolean needsNativeAccess() { + return true; + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/nfi/NFIIsolatedNativeAccess.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/nfi/NFIIsolatedNativeAccess.java index 0fc2560fb303..f9eea4185c37 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/nfi/NFIIsolatedNativeAccess.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/nfi/NFIIsolatedNativeAccess.java @@ -22,12 +22,16 @@ */ package com.oracle.truffle.espresso.ffi.nfi; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.MemoryAllocationException; + import java.nio.file.Path; import java.util.Collections; import java.util.Objects; import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.interop.ArityException; import com.oracle.truffle.api.interop.InteropLibrary; @@ -38,16 +42,21 @@ import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.library.ExportLibrary; import com.oracle.truffle.api.library.ExportMessage; -import com.oracle.truffle.api.profiles.BranchProfile; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.profiles.InlinedBranchProfile; import com.oracle.truffle.espresso.EspressoLanguage; -import com.oracle.truffle.espresso.ffi.Buffer; import com.oracle.truffle.espresso.ffi.NativeAccess; import com.oracle.truffle.espresso.ffi.NativeSignature; import com.oracle.truffle.espresso.ffi.NativeType; import com.oracle.truffle.espresso.ffi.Pointer; +import com.oracle.truffle.espresso.ffi.RawPointer; import com.oracle.truffle.espresso.ffi.TruffleByteBuffer; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; +import com.oracle.truffle.espresso.ffi.memory.UnsafeNativeMemory; import com.oracle.truffle.espresso.impl.EmptyKeysArray; import com.oracle.truffle.espresso.meta.EspressoError; +import com.oracle.truffle.espresso.meta.Meta; +import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.substitutions.Collect; /** @@ -78,7 +87,7 @@ public final class NFIIsolatedNativeAccess extends NFINativeAccess { private final DefaultLibrary defaultLibrary; NFIIsolatedNativeAccess(TruffleLanguage.Env env) { - super(env); + super(env, new IsolatedUnsafeNativeMemory()); // libeden.so must be the first library loaded in the isolated namespace. Path espressoLibraryPath = EspressoLanguage.getEspressoLibs(env); this.edenLibrary = loadLibrary(Collections.singletonList(espressoLibraryPath), "eden", true); @@ -92,14 +101,16 @@ public final class NFIIsolatedNativeAccess extends NFINativeAccess { * because is based on calling dlsym located outside the isolated namespace. libeden.so, * loaded inside the isolated namespace provides a dlsym shim inside the namespace. */ - this.defaultLibrary = new DefaultLibrary(this.dlsym, rtldDefault()); + ((IsolatedUnsafeNativeMemory) nativeMemory).init(this); + this.defaultLibrary = new DefaultLibrary(this.dlsym, rtldDefault(), nativeMemory()); + } private TruffleObject rtldDefault() { TruffleObject edenRtldDefault = lookupAndBindSymbol(edenLibrary, "eden_RTLD_DEFAULT", NativeSignature.create(NativeType.POINTER)); try { - TruffleObject result = (TruffleObject) InteropLibrary.getUncached().execute(edenRtldDefault); - assert InteropLibrary.getUncached().isPointer(result); + TruffleObject result = (TruffleObject) UNCACHED_INTEROP.execute(edenRtldDefault); + assert UNCACHED_INTEROP.isPointer(result); return result; } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) { CompilerDirectives.transferToInterpreterAndInvalidate(); @@ -123,10 +134,12 @@ static class DefaultLibrary implements TruffleObject { final @Pointer TruffleObject dlsym; final @Pointer TruffleObject rtldDefault; + final NativeMemory nativeMemory; - DefaultLibrary(@Pointer TruffleObject dlsym, @Pointer TruffleObject rtldDefault) { + DefaultLibrary(@Pointer TruffleObject dlsym, @Pointer TruffleObject rtldDefault, NativeMemory nativeMemory) { this.dlsym = Objects.requireNonNull(dlsym); this.rtldDefault = Objects.requireNonNull(rtldDefault); + this.nativeMemory = nativeMemory; } @ExportMessage @@ -148,17 +161,33 @@ boolean hasMembers() { Object readMember(String member, @CachedLibrary("this.dlsym") InteropLibrary interop, @CachedLibrary(limit = "2") InteropLibrary isNullInterop, - @Cached BranchProfile error) throws UnknownIdentifierException { + @Bind Node node, + @Cached InlinedBranchProfile error, + @Cached InlinedBranchProfile oome) throws UnknownIdentifierException { + TruffleByteBuffer buffer = null; try { - Object result = interop.execute(dlsym, this.rtldDefault, TruffleByteBuffer.allocateDirectStringUTF8(member)); + // prepare interop call by creating the string buffer + try { + buffer = TruffleByteBuffer.allocateDirectStringUTF8(member, nativeMemory); + } catch (MemoryAllocationException e) { + oome.enter(node); + EspressoContext context = EspressoContext.get(node); + Meta meta = context.getMeta(); + throw meta.throwExceptionWithMessage(meta.java_lang_OutOfMemoryError, e.getMessage(), context); + } + Object result = interop.execute(dlsym, this.rtldDefault, buffer); if (isNullInterop.isNull(result)) { - error.enter(); + error.enter(node); throw UnknownIdentifierException.create(member); } return result; } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) { CompilerDirectives.transferToInterpreterAndInvalidate(); throw EspressoError.shouldNotReachHere(e); + } finally { + if (buffer != null) { + buffer.close(); + } } } @@ -169,56 +198,6 @@ Object toDisplayString(boolean allowSideEffects) { } } - @Override - public @Buffer TruffleObject allocateMemory(long size) { - if (size < 0) { - throw new IllegalArgumentException("negative buffer length: " + size); - } - try { - @Pointer - TruffleObject address = (TruffleObject) UNCACHED_INTEROP.execute(malloc, size); - if (InteropLibrary.getUncached().isNull(address)) { - // malloc returned NULL - return null; - } - return TruffleByteBuffer.wrap(address, Math.toIntExact(size)); - } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw EspressoError.shouldNotReachHere(e); - } - } - - @Override - public void freeMemory(@Pointer TruffleObject buffer) { - assert InteropLibrary.getUncached().isPointer(buffer); - try { - UNCACHED_INTEROP.execute(free, buffer); - } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw EspressoError.shouldNotReachHere(e); - } - } - - @Override - public @Buffer TruffleObject reallocateMemory(@Pointer TruffleObject buffer, long newSize) { - if (newSize < 0) { - throw new IllegalArgumentException("negative buffer length: " + newSize); - } - assert InteropLibrary.getUncached().isPointer(buffer); - try { - @Pointer - TruffleObject address = (TruffleObject) UNCACHED_INTEROP.execute(realloc, buffer, newSize); - if (InteropLibrary.getUncached().isNull(address)) { - // realloc returned NULL - return null; - } - return TruffleByteBuffer.wrap(address, Math.toIntExact(newSize)); - } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw EspressoError.shouldNotReachHere(e); - } - } - @Override public void prepareThread() { try { @@ -244,4 +223,64 @@ public NativeAccess create(TruffleLanguage.Env env) { return new NFIIsolatedNativeAccess(env); } } + + private static final class IsolatedUnsafeNativeMemory extends UnsafeNativeMemory { + /* + * This can not be final since it would leak the "this" reference before the class is + * initialized. + */ + @CompilationFinal private NFIIsolatedNativeAccess nfiIsolatedNativeAccess; + + public void init(NFIIsolatedNativeAccess nativeAccess) { + assert this.nfiIsolatedNativeAccess == null; + this.nfiIsolatedNativeAccess = nativeAccess; + } + + @Override + public long allocateMemory(long bytes) { + long size = bytes == 0 ? 1 : bytes; + try { + @Pointer + TruffleObject address = (TruffleObject) UNCACHED_INTEROP.execute(nfiIsolatedNativeAccess.malloc, size); + if (UNCACHED_INTEROP.isNull(address)) { + // malloc returned NULL + return 0L; + } + return UNCACHED_INTEROP.asPointer(address); + } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere(e); + } + } + + @Override + public void freeMemory(long address) { + if (address == 0) { + return; + } + try { + UNCACHED_INTEROP.execute(nfiIsolatedNativeAccess.free, RawPointer.create(address)); + } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere(e); + } + } + + @Override + public long reallocateMemory(long address, long newSize) { + long size = newSize == 0 ? 1 : newSize; + try { + @Pointer + TruffleObject newAddress = (TruffleObject) UNCACHED_INTEROP.execute(nfiIsolatedNativeAccess.realloc, address, size); + if (UNCACHED_INTEROP.isNull(address)) { + // realloc returned NULL + return 0L; + } + return UNCACHED_INTEROP.asPointer(newAddress); + } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere(e); + } + } + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/nfi/NFINativeAccess.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/nfi/NFINativeAccess.java index ef882dc0f10b..63125a4062d0 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/nfi/NFINativeAccess.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/nfi/NFINativeAccess.java @@ -51,22 +51,18 @@ import com.oracle.truffle.api.source.Source; import com.oracle.truffle.espresso.EspressoLanguage; import com.oracle.truffle.espresso.classfile.perf.DebugCounter; -import com.oracle.truffle.espresso.ffi.Buffer; import com.oracle.truffle.espresso.ffi.NativeAccess; import com.oracle.truffle.espresso.ffi.NativeSignature; import com.oracle.truffle.espresso.ffi.NativeType; import com.oracle.truffle.espresso.ffi.Pointer; -import com.oracle.truffle.espresso.ffi.RawPointer; import com.oracle.truffle.espresso.ffi.SignatureCallNode; -import com.oracle.truffle.espresso.ffi.TruffleByteBuffer; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; +import com.oracle.truffle.espresso.ffi.memory.UnsafeNativeMemory; import com.oracle.truffle.espresso.meta.EspressoError; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; import com.oracle.truffle.espresso.substitutions.Collect; -import com.oracle.truffle.espresso.vm.UnsafeAccess; import com.oracle.truffle.nfi.api.SignatureLibrary; -import sun.misc.Unsafe; - /** * Espresso native interface implementation based on TruffleNFI, this class is fully functional on * its own (nfi-native backend) and also serves as base for other NFI backends. @@ -75,13 +71,14 @@ public class NFINativeAccess implements NativeAccess { protected static final TruffleLogger LOGGER = TruffleLogger.getLogger(EspressoLanguage.ID, NFINativeAccess.class); protected static final InteropLibrary UNCACHED_INTEROP = InteropLibrary.getUncached(); protected static final SignatureLibrary UNCACHED_SIGNATURE = SignatureLibrary.getUncached(); - private static final Unsafe UNSAFE = UnsafeAccess.get(); private static final boolean CACHE_SIGNATURES = "true".equals(System.getProperty("espresso.nfi.cache_signatures", "true")); private static final DebugCounter NFI_SIGNATURES_CREATED = DebugCounter.create("NFI signatures created"); private final Map signatureCache; protected final TruffleLanguage.Env env; + protected final NativeMemory nativeMemory; + protected static String nfiType(NativeType nativeType) { // @formatter:off switch (nativeType) { @@ -147,7 +144,12 @@ protected final Object getOrCreateNFISignature(NativeSignature nativeSignature, } NFINativeAccess(TruffleLanguage.Env env) { + this(env, new UnsafeNativeMemory()); + } + + NFINativeAccess(TruffleLanguage.Env env, NativeMemory nativeMemory) { this.env = env; + this.nativeMemory = nativeMemory; signatureCache = CACHE_SIGNATURES ? new ConcurrentHashMap<>() : null; @@ -467,49 +469,6 @@ public void prepareThread() { // nop } - @Override - public @Buffer TruffleObject allocateMemory(long size) { - long address = 0L; - try { - address = UNSAFE.allocateMemory(size); - } catch (OutOfMemoryError e) { - return null; - } - return TruffleByteBuffer.wrap(RawPointer.create(address), Math.toIntExact(size)); - } - - @Override - public @Buffer TruffleObject reallocateMemory(@Pointer TruffleObject buffer, long newSize) { - assert InteropLibrary.getUncached().isPointer(buffer); - long oldAddress = 0L; - try { - oldAddress = InteropLibrary.getUncached().asPointer(buffer); - } catch (UnsupportedMessageException e) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw EspressoError.shouldNotReachHere(e); - } - long newAddress = 0L; - try { - newAddress = UNSAFE.reallocateMemory(oldAddress, newSize); - } catch (OutOfMemoryError e) { - return null; - } - return TruffleByteBuffer.wrap(RawPointer.create(newAddress), Math.toIntExact(newSize)); - } - - @Override - public void freeMemory(@Pointer TruffleObject buffer) { - assert InteropLibrary.getUncached().isPointer(buffer); - long address = 0L; - try { - address = InteropLibrary.getUncached().asPointer(buffer); - } catch (UnsupportedMessageException e) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw EspressoError.shouldNotReachHere(e); - } - UNSAFE.freeMemory(address); - } - @Collect(NativeAccess.class) public static final class Provider implements NativeAccess.Provider { @@ -526,4 +485,8 @@ public NativeAccess create(TruffleLanguage.Env env) { } } + @Override + public NativeMemory nativeMemory() { + return nativeMemory; + } } From 54d5567ec31d41c0d1de4739cf775dea790d4642 Mon Sep 17 00:00:00 2001 From: Elia Trachsel Date: Wed, 24 Sep 2025 12:52:28 +0200 Subject: [PATCH 2/3] Introduction of NativeMemory to EspressoLibs: Basically just makes EspressoLibs use the newly added NativeMemory interface. Meaning all function that involved off-heap accesses were rewritten. Additionally the functionaily to read and write from certain address in TruffleIO was added. --- .../truffle/espresso/ffi/nfi/NativeUtils.java | 27 --- .../oracle/truffle/espresso/io/TruffleIO.java | 222 +++++++++++++++--- .../impl/Target_java_lang_ClassLoader.java | 23 +- .../Target_sun_nio_ch_FileDispatcherImpl.java | 62 ++--- .../Target_sun_nio_ch_TruffleDispatcher.java | 25 +- .../impl/Target_java_util_zip_Inflater.java | 59 +++-- 6 files changed, 291 insertions(+), 127 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/nfi/NativeUtils.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/nfi/NativeUtils.java index 7bebdbf9ac0c..f912232de1fd 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/nfi/NativeUtils.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/nfi/NativeUtils.java @@ -22,8 +22,6 @@ */ package com.oracle.truffle.espresso.ffi.nfi; -import static com.oracle.truffle.espresso.substitutions.standard.Target_sun_misc_Unsafe.ADDRESS_SIZE; - import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.IntBuffer; @@ -185,29 +183,4 @@ public static String fromUTF8Ptr(long rawBytesPtr) { public static ByteBuffer allocateDirect(int capacity) { return ByteBuffer.allocateDirect(capacity).order(ByteOrder.nativeOrder()); } - - @TruffleBoundary - public static ByteBuffer[] getByteBuffersFromIOVec(long address, int len) { - // constants - int lenOffset = ADDRESS_SIZE; - int sizeOfIOVec = (short) (ADDRESS_SIZE * 2); - int baseOffset = 0; - - long curIOVecAddr = address; - long nextAddr; - long nextLen; - ByteBuffer[] buffs = new ByteBuffer[len]; - for (int i = 0; i < len; i++) { - if (ADDRESS_SIZE == 4) { - nextAddr = UNSAFE.getInt(curIOVecAddr + baseOffset); - nextLen = UNSAFE.getInt(curIOVecAddr + lenOffset); - } else { - nextAddr = UNSAFE.getLong(curIOVecAddr + baseOffset); - nextLen = UNSAFE.getLong(curIOVecAddr + lenOffset); - } - buffs[i] = directByteBuffer(nextAddr, nextLen); - curIOVecAddr += sizeOfIOVec; - } - return buffs; - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/TruffleIO.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/TruffleIO.java index c1541c50c73e..223643f70040 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/TruffleIO.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/TruffleIO.java @@ -22,7 +22,10 @@ */ package com.oracle.truffle.espresso.io; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.IllegalMemoryAccessException; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.MemoryAccessMode; import static com.oracle.truffle.espresso.libs.libnio.impl.Target_sun_nio_ch_IOUtil.FD_LIMIT; +import static com.oracle.truffle.espresso.substitutions.standard.Target_sun_misc_Unsafe.ADDRESS_SIZE; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -58,6 +61,8 @@ import java.nio.file.StandardOpenOption; import java.nio.file.attribute.FileAttribute; import java.nio.file.attribute.PosixFilePermission; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -74,6 +79,7 @@ import com.oracle.truffle.espresso.descriptors.EspressoSymbols.Names; import com.oracle.truffle.espresso.descriptors.EspressoSymbols.Signatures; import com.oracle.truffle.espresso.descriptors.EspressoSymbols.Types; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; import com.oracle.truffle.espresso.impl.ContextAccess; import com.oracle.truffle.espresso.impl.Field; import com.oracle.truffle.espresso.impl.Method; @@ -657,8 +663,9 @@ public long length(int fd) { * @param self The file descriptor holder. * @param fdAccess How to get the file descriptor from the holder. * @param bytes The byte buffer containing the bytes to write. - * @return The number of bytes written, possibly zero. - * @see java.io.FileOutputStream#write(byte[]) + * @return The number of bytes written, possibly zero. If the channel is in non-blocking mode + * and the operation would block {@link IOStatus_Sync#UNAVAILABLE} is returned. + * @see WritableByteChannel#write(ByteBuffer) */ @TruffleBoundary public int writeBytes(@JavaType(Object.class) StaticObject self, @@ -667,6 +674,29 @@ public int writeBytes(@JavaType(Object.class) StaticObject self, return writeBytes(getFD(self, fdAccess), bytes); } + /** + * Writes bytes from the address specified to the file associated with the given file descriptor + * holder in plain mode. + * + * @param self The file descriptor holder. + * @param fdAccess How to get the file descriptor from the holder. + * @param address The address containing the bytes to write. + * @param length the number of bytes to write. + * @return The number of bytes written, possibly zero. If the channel is in non-blocking mode + * and the operation would block {@link IOStatus_Sync#UNAVAILABLE} is returned. + * @see java.io.FileOutputStream#write(byte[]) + */ + @TruffleBoundary + public int writeAddress(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess, + long address, int length) { + try { + return writeBytes(self, fdAccess, context.getNativeAccess().nativeMemory().wrapNativeMemory(address, length)); + } catch (IllegalMemoryAccessException e) { + throw Throw.throwIOException("Invalid memory access: Trying to access memory outside the allocated region", getContext()); + } + } + /** * Writes buffered bytes to the file associated with the given file descriptor holder. * @@ -675,7 +705,8 @@ public int writeBytes(@JavaType(Object.class) StaticObject self, * @param bytes The byte array containing the bytes to write. * @param off The start of the byte sequence to write from {@code bytes}. * @param len The length of the byte sequence to write. - * @return The number of bytes written, possibly zero. + * @return The number of bytes written, possibly zero. If the channel is in non-blocking mode + * and the operation would block {@link IOStatus_Sync#UNAVAILABLE} is returned. * @see java.io.FileOutputStream#write(byte[], int, int) */ @TruffleBoundary @@ -713,65 +744,67 @@ public int writeBytes(int fd, } /** - * Writes the content of the ByteBuffers to the file associated with the given file descriptor - * holder in the exact order of the ByteBuffers array. + * Writes the content of the underlying "native" ByteBuffers to the file associated with the + * given file descriptor holder in the exact order of the ByteBuffers array. * * @param self The file descriptor holder. * @param fdAccess How to get the file descriptor from the holder. - * @param buffers The ByteBuffer containing the bytes to write. + * @param address The base address of the continuous memory region, which contains addresses and + * lengths for the ByteBuffers we write from. + * @param length the number of ByteBuffers to extract from address. * @return The number of bytes written, possibly zero. * @see java.nio.channels.GatheringByteChannel#write(ByteBuffer[]) */ @TruffleBoundary - public long writeByteBuffers(@JavaType(Object.class) StaticObject self, + public long writev(@JavaType(Object.class) StaticObject self, FDAccess fdAccess, - ByteBuffer[] buffers) { - Channel channel = Checks.ensureOpen(getChannel(getFD(self, fdAccess)), getContext()); + long address, int length) { + NativeMemory nativeMemory = context.getNativeAccess().nativeMemory(); + AddressLengthPair[] addressLengthPairs = extractAddressLengthPairs(address, length, nativeMemory); + StaticObject fileDesc = getFileDesc(self, fdAccess); + Channel channel = Checks.ensureOpen(getChannel(getFD(fileDesc)), getContext()); if (channel instanceof GatheringByteChannel gatheringByteChannel) { try { - return gatheringByteChannel.write(buffers); + return gatheringByteChannel.write(asByteBuffer(addressLengthPairs, nativeMemory)); } catch (IOException e) { throw Throw.throwIOException(e, context); } } else { - context.getLogger().warning(() -> "No GatheringByteChannel for writev operation!" + channel.getClass()); - long ret = 0; - for (ByteBuffer buf : buffers) { - ret += writeBytes(self, fdAccess, buf); - } - return ret; + LibsState.getLogger().warning("No GatheringByteChannel for writev operation! You are using: " + channel.getClass()); } + return sequentialWritev(self, fdAccess, addressLengthPairs); } /** - * Reads the content of the file associated with the given file descriptor into the provided - * ByteBuffers sequentially. + * Reads the content of the file associated with the given file descriptor into the underlying + * "native" ByteBuffers. * * @param self The file descriptor holder. * @param fdAccess How to get the file descriptor from the holder. - * @param buffers The ByteBuffers we read data into. + * @param address The base address of the continuous memory region, which contains addresses and + * lengths for the ByteBuffers we read into. + * @param length the number of ByteBuffers to extract from address. * @return The number of bytes written, possibly zero. * @see java.nio.channels.ScatteringByteChannel#read(ByteBuffer) */ @TruffleBoundary - public long readByteBuffers(@JavaType(Object.class) StaticObject self, + public long readv(@JavaType(Object.class) StaticObject self, FDAccess fdAccess, - ByteBuffer[] buffers) { - Channel channel = Checks.ensureOpen(getChannel(getFD(self, fdAccess)), getContext()); + long address, int length) { + NativeMemory nativeMemory = context.getNativeAccess().nativeMemory(); + AddressLengthPair[] addressLengthPairs = extractAddressLengthPairs(address, length, nativeMemory); + StaticObject fileDesc = getFileDesc(self, fdAccess); + Channel channel = Checks.ensureOpen(getChannel(getFD(fileDesc)), getContext()); if (channel instanceof ScatteringByteChannel scatteringByteChannel) { try { - return scatteringByteChannel.read(buffers); + return scatteringByteChannel.read(asByteBuffer(addressLengthPairs, nativeMemory)); } catch (IOException e) { throw Throw.throwIOException(e, context); } } else { - context.getLogger().warning(() -> "No ScatteringByteChannel for readv operation!" + channel.getClass()); - long ret = 0; - for (ByteBuffer buf : buffers) { - ret += readBytes(self, fdAccess, buf); - } - return ret; + LibsState.getLogger().warning("No ScatteringByteChannel for readv operation! You are using: " + channel.getClass()); } + return sequentialReadv(self, fdAccess, addressLengthPairs); } /** @@ -779,7 +812,9 @@ public long readByteBuffers(@JavaType(Object.class) StaticObject self, * * @param self The file descriptor holder. * @param fdAccess How to get the file descriptor from the holder. - * @return The byte read, or {@code -1} if reading failed. + * @return The byte read, or -1 if the channel has reached end-of-stream. If the channel is in + * non-blocking mode and no data is available {@link IOStatus_Sync#UNAVAILABLE} is + * returned. * @see FileInputStream#read() */ @TruffleBoundary @@ -813,8 +848,9 @@ public int readSingle(int fd) { * @param off The start of the byte sequence to write to in {@code bytes}. * @param len The length of the byte sequence to read. * @return The number of bytes read, possibly zero, or -1 if the channel has reached - * end-of-stream - * @see java.io.FileInputStream#read(byte[], int, int) + * end-of-stream. If the channel is in non-blocking mode and no data is available + * {@link IOStatus_Sync#UNAVAILABLE} is returned. + * @see ReadableByteChannel#read(ByteBuffer) */ @TruffleBoundary public int readBytes(@JavaType(Object.class) StaticObject self, @@ -838,7 +874,8 @@ public int readBytes(@JavaType(Object.class) StaticObject self, * @param fdAccess How to get the file descriptor from the holder. * @param buffer The ByteBuffer that will contain the bytes read. * @return The number of bytes read, possibly zero, or -1 if the channel has reached - * end-of-stream + * end-of-stream. If the channel is in non-blocking mode and no data is available + * {@link IOStatus_Sync#UNAVAILABLE} is returned. * @see java.io.FileInputStream#read(byte[], int, int) */ @TruffleBoundary @@ -867,6 +904,25 @@ public int readBytes(int fd, } } + /** + * Reads a byte sequence from the file associated with the given file descriptor into the given + * memory address in plain mode. + * + * @param addr the address to read into + * @param length how many bytes to read + * @see #readBytes(StaticObject, FDAccess, byte[], int, int) + */ + @TruffleBoundary + public int readAddress(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess, long addr, + int length) { + try { + return readBytes(self, fdAccess, context.getNativeAccess().nativeMemory().wrapNativeMemory(addr, length)); + } catch (IllegalMemoryAccessException e) { + throw Throw.throwIOException("Invalid memory access: Trying to access memory outside the allocated region", getContext()); + } + } + /** * Drains the content of the file associated with the given file descriptor holder. That means * reading the entire file, but discarding all that's read. @@ -1280,6 +1336,106 @@ private int nextFreeFd() { return nextFd; } + private long sequentialWritev(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess, AddressLengthPair[] addressLengthPairs) { + long ret = 0; + for (AddressLengthPair addressLengthPair : addressLengthPairs) { + long currAddr = addressLengthPair.address(); + long currLength = addressLengthPair.length(); + long currWritten = 0; + do { + long nextWrite = Math.min(Integer.MAX_VALUE, currLength - currWritten); + // unchecked cast is safe since nextWrite <= Integer.MAX_VALUE + int written = writeAddress(self, fdAccess, currAddr + currWritten, (int) nextWrite); + if (written <= 0 && currLength > currWritten) { + /* + * The writev() function shall always write a complete area before proceeding to + * the next. + */ + return ret + currWritten; + } + currWritten += written; + } while (currWritten < currLength); + ret += currWritten; + } + return ret; + } + + private long sequentialReadv(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess, AddressLengthPair[] addressLengthPairs) { + long ret = 0; + for (AddressLengthPair addressLengthPair : addressLengthPairs) { + long currAddr = addressLengthPair.address(); + long currLength = addressLengthPair.length(); + long currRead = 0; + do { + long nextRead = Math.min(Integer.MAX_VALUE, currLength - currRead); + // unchecked cast is safe since nextRead <= Integer.MAX_VALUE + int read = readAddress(self, fdAccess, currAddr + currRead, (int) nextRead); + if (read <= 0 && currRead < currLength) { + /* + * readv() completely fills iov[0] before proceeding to iov[1], and so on. + */ + return ret + currRead; + } + currRead += read; + } while (currRead < currLength); + ret += currRead; + } + return ret; + } + + @TruffleBoundary + private ByteBuffer[] asByteBuffer(AddressLengthPair[] addressLengthPairs, NativeMemory nativeMemory) { + List buffs = new ArrayList<>(addressLengthPairs.length); + for (int i = 0; i < addressLengthPairs.length; i++) { + long bytesToWrap = addressLengthPairs[i].length; + long currAddr = addressLengthPairs[i].address(); + long wrappedBytes = 0; + do { + long nextWrap = Math.min(Integer.MAX_VALUE, bytesToWrap - wrappedBytes); + // unchecked cast is safe as nextWrap <= Integer.MAX_VALUE + try { + buffs.add(nativeMemory.wrapNativeMemory(currAddr + wrappedBytes, (int) nextWrap)); + } catch (IllegalMemoryAccessException e) { + throw Throw.throwIOException("Invalid memory access: Trying to access memory outside the allocated region", getContext()); + } + wrappedBytes += nextWrap; + } while (wrappedBytes < bytesToWrap); + } + return buffs.toArray(new ByteBuffer[0]); + } + + private record AddressLengthPair(long address, long length) { + } + + private AddressLengthPair[] extractAddressLengthPairs(long address, int len, NativeMemory nativeMemory) { + int lenOffset = ADDRESS_SIZE; + int sizeOfIOVec = (short) (ADDRESS_SIZE * 2); + long curIOVecAddr = address; + long nextAddr; + long nextLen; + long fullLen = 0; + AddressLengthPair[] addressLengthPairs = new AddressLengthPair[len]; + for (int i = 0; i < len; i++) { + try { + if (ADDRESS_SIZE == 4) { + nextAddr = nativeMemory.getInt(curIOVecAddr, MemoryAccessMode.PLAIN); + nextLen = nativeMemory.getInt(curIOVecAddr + lenOffset, MemoryAccessMode.PLAIN); + } else { + nextAddr = nativeMemory.getLong(curIOVecAddr, MemoryAccessMode.PLAIN); + nextLen = nativeMemory.getLong(curIOVecAddr + lenOffset, MemoryAccessMode.PLAIN); + } + } catch (IllegalMemoryAccessException e) { + throw Throw.throwIOException("Invalid memory access: Trying to access memory outside the allocated region", getContext()); + } + fullLen = Math.addExact(fullLen, nextLen); + addressLengthPairs[i] = new AddressLengthPair(nextAddr, nextLen); + curIOVecAddr += sizeOfIOVec; + } + return addressLengthPairs; + } + private ReadableByteChannel getReadableChannel(@JavaType(Object.class) StaticObject self, FDAccess fdAccess) { return getReadableChannel(getFD(getFileDesc(self, fdAccess))); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_ClassLoader.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_ClassLoader.java index 7ae90c7f9d8f..e60095d00ce5 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_ClassLoader.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_ClassLoader.java @@ -22,6 +22,8 @@ */ package com.oracle.truffle.espresso.libs.libjava.impl; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.IllegalMemoryAccessException; + import java.nio.ByteBuffer; import java.security.ProtectionDomain; @@ -157,17 +159,28 @@ public static void registerNatives() { // retrieve the GuestBuffer as Array @Pointer TruffleObject dataAddrPointer = ctx.getJNI().GetDirectBufferAddress(data); - ByteBuffer dataBuffer = NativeUtils.directByteBuffer(dataAddrPointer, len + off); + /* + * This refers to a NativeMemory address since the guest ByteBuffer was allocated using + * Unsafe.allocateMemory which we substitute to go through native memory. + */ + long rawDataPtr = NativeUtils.interopAsPointer(dataAddrPointer); + byte[] buf = new byte[len]; + // reads the memory in the buffer in plain mode (as does the native function). + try { + ctx.getNativeAccess().nativeMemory().readMemory(rawDataPtr + off, buf); + } catch (IllegalMemoryAccessException e) { + /* + * If we reach here it indicate we try to read outside the boundaries of the allocated + * directBuffer which suggests an IndexOutOfBoundsException. + */ + throw ctx.getMeta().throwIndexOutOfBoundsExceptionBoundary("ByteBuffer", off, len); + } Symbol type = null; if (StaticObject.notNull(name)) { type = ctx.getVM().nameToInternal(toSlashName(ctx.getMeta().toHostString(name))); } - // read into array - byte[] buf = new byte[len]; - dataBuffer.get(off, buf); - return ctx.getVM().defineClass(type, loader, pd, buf); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_FileDispatcherImpl.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_FileDispatcherImpl.java index 21e149a2ec51..0cda49064e47 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_FileDispatcherImpl.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_FileDispatcherImpl.java @@ -22,23 +22,19 @@ */ package com.oracle.truffle.espresso.libs.libnio.impl; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.IllegalMemoryAccessException; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.MemoryAllocationException; + import java.io.FileDescriptor; import java.io.IOException; -import java.nio.ByteBuffer; -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.TruffleObject; -import com.oracle.truffle.api.interop.UnsupportedMessageException; -import com.oracle.truffle.espresso.ffi.Buffer; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.espresso.ffi.NativeAccess; -import com.oracle.truffle.espresso.ffi.RawPointer; -import com.oracle.truffle.espresso.ffi.nfi.NativeUtils; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; import com.oracle.truffle.espresso.io.FDAccess; import com.oracle.truffle.espresso.io.Throw; import com.oracle.truffle.espresso.io.TruffleIO; import com.oracle.truffle.espresso.libs.libnio.LibNio; -import com.oracle.truffle.espresso.meta.EspressoError; import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; @@ -64,36 +60,40 @@ public static long allocationGranularity0(@Inject Meta meta) { @Substitution @Throws(IOException.class) @SuppressWarnings("unused") + @TruffleBoundary public static long map0(@JavaType(FileDescriptor.class) StaticObject fd, int prot, long position, long length, boolean isSync, @Inject TruffleIO io, @Inject EspressoContext ctx) { - + // mmap syscall expects length to be > 0 + if (length <= 0) { + throw Throw.throwIllegalArgumentException("length must be greater 0", ctx); + } if (prot == io.fileChannelImplSync.MAP_RW) { // We don't allow public writes since we don't actually map the file. throw Throw.throwUnsupported("mmap for public writes is not supported at the moment", ctx); } - @Buffer - TruffleObject buffer = ctx.getNativeAccess().allocateMemory(length); - long addr; + NativeMemory nativeMemory = ctx.getNativeAccess().nativeMemory(); + long addr = 0; try { - addr = InteropLibrary.getUncached().asPointer(buffer); - } catch (UnsupportedMessageException e) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw EspressoError.shouldNotReachHere(e); + addr = nativeMemory.allocateMemory(length); + } catch (MemoryAllocationException e) { + throw Throw.throwIOException("An exception occurred while allocating memory:" + e, ctx); } long oldPosition = io.position(fd, FDAccess.forFileDescriptor()); assert oldPosition >= 0; try { - // creates a byteBuffer that can hold up to length - ByteBuffer byteBuffer = NativeUtils.directByteBuffer(addr, length); - // adjust the position of the underlying Channel and read io.seek(fd, FDAccess.forFileDescriptor(), position); - // read until byteBuffer is full or we reach EOF. + long read = 0; int nextRead; do { - nextRead = io.readBytes(fd, FDAccess.forFileDescriptor(), byteBuffer); - read += nextRead; + // Calculate how much we can read in this iteration (max Integer.MAX_VALUE) + int chunkSize = (int) Math.min(length - read, Integer.MAX_VALUE); + nextRead = io.readAddress(fd, FDAccess.forFileDescriptor(), addr + read, chunkSize); + + if (nextRead > 0) { + read += nextRead; + } } while (nextRead != io.ioStatusSync.EOF && read < length); } finally { // always reset the position @@ -104,8 +104,12 @@ public static long map0(@JavaType(FileDescriptor.class) StaticObject fd, int pro @Substitution @SuppressWarnings("unused") - public static int unmap0(long address, long length, @Inject NativeAccess nativeAccess) { - nativeAccess.freeMemory(RawPointer.create(address)); + public static int unmap0(long address, long length, @Inject NativeAccess nativeAccess, @Inject Meta meta) { + try { + nativeAccess.nativeMemory().freeMemory(address); + } catch (IllegalMemoryAccessException e) { + throw meta.throwIllegalArgumentExceptionBoundary("Invalid memory access: address should refer to a value returned by allocate!"); + } return 0; } @@ -176,11 +180,15 @@ public static long size0(@JavaType(FileDescriptor.class) StaticObject fd, @Injec public static int pread0(@JavaType(FileDescriptor.class) StaticObject fd, long address, int len, long position, @Inject TruffleIO io) { // Currently, this is not thread safe as we temporarily update the position of the file. - ByteBuffer dst = NativeUtils.directByteBuffer(address, len); long oldPos = io.position(fd, FDAccess.forFileDescriptor()); try { io.seek(fd, FDAccess.forFileDescriptor(), position); - return io.readBytes(fd, FDAccess.forFileDescriptor(), dst); + /* + * This might not read the full requested length which is acceptable according to the + * documentation of the syscall pread. Additionally native method reads the memory in + * plain mode. + */ + return io.readAddress(fd, FDAccess.forFileDescriptor(), address, len); } finally { io.seek(fd, FDAccess.forFileDescriptor(), oldPos); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_TruffleDispatcher.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_TruffleDispatcher.java index 864381614306..053667afe58a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_TruffleDispatcher.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_TruffleDispatcher.java @@ -24,9 +24,7 @@ import java.io.FileDescriptor; import java.io.IOException; -import java.nio.ByteBuffer; -import com.oracle.truffle.espresso.ffi.nfi.NativeUtils; import com.oracle.truffle.espresso.io.FDAccess; import com.oracle.truffle.espresso.io.TruffleIO; import com.oracle.truffle.espresso.libs.libnio.LibNio; @@ -44,31 +42,36 @@ public final class Target_sun_nio_ch_TruffleDispatcher { @Throws(IOException.class) public static int read0(@JavaType(FileDescriptor.class) StaticObject fd, long address, int len, @Inject TruffleIO io) { - ByteBuffer dst = NativeUtils.directByteBuffer(address, len); - return io.readBytes(fd, FDAccess.forFileDescriptor(), dst); + /* + * This might not read the full requested length which is acceptable according to the + * documentation of the syscall read. Additionally native method reads the memory in plain + * mode. + */ + return io.readAddress(fd, FDAccess.forFileDescriptor(), address, len); } @Substitution @Throws(IOException.class) public static long readv0(@JavaType(FileDescriptor.class) StaticObject fd, long address, int len, @Inject TruffleIO io) { - ByteBuffer[] buffers = NativeUtils.getByteBuffersFromIOVec(address, len); - return io.readByteBuffers(fd, FDAccess.forFileDescriptor(), buffers); + return io.readv(fd, FDAccess.forFileDescriptor(), address, len); } @Substitution @Throws(IOException.class) public static int write0(@JavaType(FileDescriptor.class) StaticObject fd, long address, int len, @Inject TruffleIO io) { - ByteBuffer dst = NativeUtils.directByteBuffer(address, len); - return io.writeBytes(fd, FDAccess.forFileDescriptor(), dst); + /* + * This might not write the full requested length which is acceptable according to the + * documentation of the syscall write. Additionally native method accesses the memory in + * plain mode. + */ + return io.writeAddress(fd, FDAccess.forFileDescriptor(), address, len); } @Substitution @Throws(IOException.class) public static long writev0(@JavaType(FileDescriptor.class) StaticObject fd, long address, int len, @Inject TruffleIO io) { - // len is length of IOV - ByteBuffer[] buffers = NativeUtils.getByteBuffersFromIOVec(address, len); - return io.writeByteBuffers(fd, FDAccess.forFileDescriptor(), buffers); + return io.writev(fd, FDAccess.forFileDescriptor(), address, len); } @Substitution diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libzip/impl/Target_java_util_zip_Inflater.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libzip/impl/Target_java_util_zip_Inflater.java index 13958f085f9e..42a0eb2d5625 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libzip/impl/Target_java_util_zip_Inflater.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libzip/impl/Target_java_util_zip_Inflater.java @@ -22,16 +22,18 @@ */ package com.oracle.truffle.espresso.libs.libzip.impl; -import java.nio.ByteBuffer; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.IllegalMemoryAccessException; + import java.util.zip.DataFormatException; import java.util.zip.Inflater; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.espresso.EspressoLanguage; -import com.oracle.truffle.espresso.ffi.nfi.NativeUtils; import com.oracle.truffle.espresso.libs.LibsMeta; import com.oracle.truffle.espresso.libs.LibsState; import com.oracle.truffle.espresso.libs.libzip.LibZip; +import com.oracle.truffle.espresso.meta.Meta; +import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; import com.oracle.truffle.espresso.substitutions.Inject; @@ -73,9 +75,13 @@ public static void setDictionary(long addr, @JavaType(byte[].class) StaticObject } @Substitution - public static void setDictionaryBuffer(long addr, long bufAddress, int len, @Inject LibsState libsState) { - ByteBuffer dst = NativeUtils.directByteBuffer(bufAddress, len); - libsState.getInflater(addr).setDictionary(dst); + @TruffleBoundary + public static void setDictionaryBuffer(long addr, long bufAddress, int len, @Inject LibsState libsState, @Inject EspressoContext ctx, @Inject Meta meta) { + try { + libsState.getInflater(addr).setDictionary(ctx.getNativeAccess().nativeMemory().wrapNativeMemory(bufAddress, len)); + } catch (IllegalMemoryAccessException e) { + throw meta.throwIllegalArgumentExceptionBoundary("Invalid memory access: bufAddress and len refer to memory outside the allocated region"); + } } @Substitution(hasReceiver = true) @@ -106,10 +112,11 @@ public static long inflateBytesBytes(@JavaType(Inflater.class) StaticObject gues @Substitution(hasReceiver = true) public static long inflateBytesBuffer(@JavaType(Inflater.class) StaticObject guestInflater, long addr, @JavaType(byte[].class) StaticObject inputArray, int inputOff, int inputLen, - long outputAddress, int outputLen, @Inject LibsState libsState, @Inject LibsMeta libsMeta, @Inject EspressoLanguage lang) { + long outputAddress, int outputLen, + @Inject LibsState libsState, @Inject LibsMeta libsMeta, + @Inject EspressoLanguage language, @Inject EspressoContext ctx) { // get Input/Output Array/Buffer - byte[] inputByteArray = inputArray.unwrap(lang); - ByteBuffer outputByteBuffer = NativeUtils.directByteBuffer(outputAddress, outputLen); + byte[] inputByteArray = inputArray.unwrap(language); // get host Inflater and set Input Inflater hostInflater = libsState.getInflater(addr); hostInflater.setInput(inputByteArray, inputOff, inputLen); @@ -117,12 +124,15 @@ public static long inflateBytesBuffer(@JavaType(Inflater.class) StaticObject gue long bytesReadOld = hostInflater.getBytesRead(); long bytesWrittenOld = hostInflater.getBytesWritten(); try { - int written = hostInflater.inflate(outputByteBuffer); + // do inflate and encode result + int written = hostInflater.inflate(ctx.getNativeAccess().nativeMemory().wrapNativeMemory(outputAddress, outputLen)); int read = Math.toIntExact(hostInflater.getBytesRead() - bytesReadOld); return encodeResult(read, written, hostInflater); } catch (DataFormatException e) { updateGuestInflater(hostInflater, bytesReadOld, bytesWrittenOld, guestInflater, libsMeta); throw libsMeta.getMeta().throwExceptionWithMessage(libsMeta.java_util_zip_DataFormatException, e.getMessage()); + } catch (IllegalMemoryAccessException e) { + throw libsMeta.getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: outputAddress and outputLen refer to memory outside the allocated region"); } } @@ -130,25 +140,26 @@ public static long inflateBytesBuffer(@JavaType(Inflater.class) StaticObject gue public static long inflateBufferBytes(@JavaType(Inflater.class) StaticObject guestInflater, long addr, long inputAddress, int inputLen, @JavaType(byte[].class) StaticObject outputArray, int outputOff, int outputLen, - @Inject LibsState libsState, @Inject LibsMeta libsMeta, - @Inject EspressoLanguage lang) { + @Inject LibsState libsState, @Inject LibsMeta libsMeta, @Inject EspressoLanguage language, + @Inject EspressoContext ctx) { - // get Input/Output Array/Buffer - ByteBuffer inputByteBuffer = NativeUtils.directByteBuffer(inputAddress, inputLen); - byte[] outputByteArray = outputArray.unwrap(lang); - // get host Inflater and set Input + // get host Inflater Inflater hostInflater = libsState.getInflater(addr); - hostInflater.setInput(inputByteBuffer); // cache bytes/read/written for the exception case long bytesReadOld = hostInflater.getBytesRead(); long bytesWrittenOld = hostInflater.getBytesWritten(); try { + // do inflate and encode result + hostInflater.setInput(ctx.getNativeAccess().nativeMemory().wrapNativeMemory(inputAddress, inputLen)); + byte[] outputByteArray = outputArray.unwrap(language); int written = hostInflater.inflate(outputByteArray, outputOff, outputLen); int read = Math.toIntExact(hostInflater.getBytesRead() - bytesReadOld); return encodeResult(read, written, hostInflater); } catch (DataFormatException e) { updateGuestInflater(hostInflater, bytesReadOld, bytesWrittenOld, guestInflater, libsMeta); throw libsMeta.getMeta().throwExceptionWithMessage(libsMeta.java_util_zip_DataFormatException, e.getMessage()); + } catch (IllegalMemoryAccessException e) { + throw libsMeta.getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: inputAddress and inputLen refer to memory outside the allocated region"); } } @@ -157,24 +168,24 @@ public static long inflateBufferBytes(@JavaType(Inflater.class) StaticObject gue public static long inflateBufferBuffer(@JavaType(Inflater.class) StaticObject guestInflater, long addr, long inputAddress, int inputLen, long outputAddress, int outputLen, - @Inject LibsState libsState, @Inject LibsMeta libsMeta, - @Inject EspressoLanguage lang) { - // get Input/Output Array/Buffer - ByteBuffer inputByteBuffer = NativeUtils.directByteBuffer(inputAddress, inputLen); - ByteBuffer outputByteBuffer = NativeUtils.directByteBuffer(outputAddress, outputLen); - // get host Inflater and set Input + @Inject LibsState libsState, @Inject LibsMeta libsMeta, @Inject EspressoLanguage language, + @Inject EspressoContext ctx) { + // get host Inflater Inflater hostInflater = libsState.getInflater(addr); - hostInflater.setInput(inputByteBuffer); // cache bytes/read/written for the exception case long bytesReadOld = hostInflater.getBytesRead(); long bytesWrittenOld = hostInflater.getBytesWritten(); try { - int written = hostInflater.inflate(outputByteBuffer); + // do inflate and encode result + hostInflater.setInput(ctx.getNativeAccess().nativeMemory().wrapNativeMemory(inputAddress, inputLen)); + int written = hostInflater.inflate(ctx.getNativeAccess().nativeMemory().wrapNativeMemory(outputAddress, outputLen)); int read = Math.toIntExact(hostInflater.getBytesRead() - bytesReadOld); return encodeResult(read, written, hostInflater); } catch (DataFormatException e) { updateGuestInflater(hostInflater, bytesReadOld, bytesWrittenOld, guestInflater, libsMeta); throw libsMeta.getMeta().throwExceptionWithMessage(libsMeta.java_util_zip_DataFormatException, e.getMessage()); + } catch (IllegalMemoryAccessException e) { + throw libsMeta.getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: an input or output address and length refers to memory outside the allocated region"); } } From b37ee736aebfa6db48bbe8b71bec5340ccae1474 Mon Sep 17 00:00:00 2001 From: Elia Trachsel Date: Wed, 12 Nov 2025 12:30:05 +0100 Subject: [PATCH 3/3] Migrate memory accesses to the NativeMemory abstraction. Ensures that all memory operations in Espresso flow through the NativeMemory layer. This mainly involves adapting Unsafe substitutions and the NativeUtils class. For the latter, we needed to ensure that all callers pass a NativeMemory instance to the called static methods. In most cases, this was straightforward as the caller had access to the context, but for the structs, it required some rewriting of the StructProcessor class. --- .../espresso/libjavavm/LibEspresso.java | 6 + .../espresso/processor/StructsProcessor.java | 22 +- .../truffle/espresso/ffi/NativeAccess.java | 52 - .../truffle/espresso/ffi/RawPointer.java | 17 + .../espresso/ffi/TruffleByteBuffer.java | 166 ++- .../truffle/espresso/ffi/nfi/NativeUtils.java | 223 ++-- .../oracle/truffle/espresso/jni/JniEnv.java | 220 ++-- .../truffle/espresso/jni/NativeEnv.java | 2 +- .../truffle/espresso/jni/RawBuffer.java | 37 +- .../truffle/espresso/jvmti/JVMTIEnv.java | 49 +- .../oracle/truffle/espresso/meta/Meta.java | 10 + .../espresso/runtime/AgentLibraries.java | 7 +- .../espresso/runtime/JImageLibrary.java | 79 +- .../runtime/panama/DowncallStubs.java | 14 +- .../Target_jdk_vm_ci_services_Services.java | 27 +- .../standard/Target_sun_misc_Unsafe.java | 1063 +++++++++++++---- .../substitutions/standard/UnsafeSupport.java | 293 ++--- .../truffle/espresso/vm/Management.java | 14 +- .../com/oracle/truffle/espresso/vm/VM.java | 92 +- .../vm/structs/JavaMemberOffsetGetter.java | 36 +- .../vm/structs/NativeMemberOffsetGetter.java | 19 +- .../espresso/vm/structs/StructStorage.java | 23 +- .../espresso/vm/structs/StructWrapper.java | 29 +- .../espresso/vm/structs/StructsAccess.java | 2 +- 24 files changed, 1630 insertions(+), 872 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso.libjavavm/src/com/oracle/truffle/espresso/libjavavm/LibEspresso.java b/espresso/src/com.oracle.truffle.espresso.libjavavm/src/com/oracle/truffle/espresso/libjavavm/LibEspresso.java index b7607568e58e..cbdfc8ee3b74 100644 --- a/espresso/src/com.oracle.truffle.espresso.libjavavm/src/com/oracle/truffle/espresso/libjavavm/LibEspresso.java +++ b/espresso/src/com.oracle.truffle.espresso.libjavavm/src/com/oracle/truffle/espresso/libjavavm/LibEspresso.java @@ -122,6 +122,12 @@ static int createJavaVM(@SuppressWarnings("unused") IsolateThread thread, JNIJav STDERR.println(" is not available in the java bindings"); return JNIErrors.JNI_ERR(); } + /* + * Here we assume the value of asNativePointer actually refers to host memory which + * generally does not hold anymore since the introduction of NativeMemory (GR-70643). + * However, this could only pose problems in the unlikely case of explicitly choosing a + * "virtualized" NativeMemory while starting espresso from native. + */ JNIJavaVM espressoJavaVM = WordFactory.pointer(java.asNativePointer()); bindings.removeMember(""); ObjectHandle contextHandle = ObjectHandles.getGlobal().create(context); diff --git a/espresso/src/com.oracle.truffle.espresso.processor/src/com/oracle/truffle/espresso/processor/StructsProcessor.java b/espresso/src/com.oracle.truffle.espresso.processor/src/com/oracle/truffle/espresso/processor/StructsProcessor.java index 892a1bd7946c..0d6b9cce9f9e 100644 --- a/espresso/src/com.oracle.truffle.espresso.processor/src/com/oracle/truffle/espresso/processor/StructsProcessor.java +++ b/espresso/src/com.oracle.truffle.espresso.processor/src/com/oracle/truffle/espresso/processor/StructsProcessor.java @@ -80,11 +80,13 @@ public class StructsProcessor extends AbstractProcessor { private static final String IMPORT_BYTEBUFFER = "java.nio.ByteBuffer"; private static final String IMPORT_JNI_HANDLES = "com.oracle.truffle.espresso.jni.JNIHandles"; private static final String IMPORT_RAW_POINTER = "com.oracle.truffle.espresso.ffi.RawPointer"; + private static final String IMPORT_NATIVE_MEMORY = "com.oracle.truffle.espresso.ffi.memory.NativeMemory"; // Classes private static final String TRUFFLE_OBJECT = "TruffleObject"; private static final String INTEROP_LIBRARY = "InteropLibrary"; private static final String JNI_HANDLES_CLASS = "JNIHandles"; + private static final String NATIVE_MEMORY_CLASS = "NativeMemory"; private static final String MEMBER_OFFSET_GETTER_CLASS = "MemberOffsetGetter"; private static final String NATIVE_MEMBER_OFFSET_GETTER_CLASS = "NativeMemberOffsetGetter"; private static final String JAVA_MEMBER_OFFSET_GETTER_CLASS = "JavaMemberOffsetGetter"; @@ -107,6 +109,7 @@ public class StructsProcessor extends AbstractProcessor { // Arguments private static final String VALUE = "valueToPut"; private static final String JNI_HANDLES_ARG = "handles"; + private static final String NATIVE_MEMORY_ARG = "nativeMemory"; private static final String PTR = "pointer"; private static final String MEMBER_OFFSET_GETTER_ARG = "offsetGetter"; private static final String MEMBER_INFO_PTR = "memberInfoPtr"; @@ -257,7 +260,7 @@ private static String generateStruct(String strName, List members, List< .inPackage(STRUCTS_PACKAGE) // .withImportGroup(Collections.singletonList(IMPORT_BYTEBUFFER)) // .withImportGroup(Collections.singletonList(IMPORT_TRUFFLE_OBJECT)) // - .withImportGroup(Arrays.asList(IMPORT_STATIC_OBJECT, IMPORT_JNI_HANDLES, IMPORT_RAW_POINTER)); + .withImportGroup(Arrays.asList(IMPORT_STATIC_OBJECT, IMPORT_JNI_HANDLES, IMPORT_NATIVE_MEMORY, IMPORT_RAW_POINTER)); String wrapperName = className + WRAPPER; @@ -324,8 +327,8 @@ private static void generateWrapper(ClassBuilder struct, List members, L MethodBuilder wrapperConstructor = new MethodBuilder(wrapperName) // .asConstructor() // - .withParams(argument(JNI_HANDLES_CLASS, JNI_HANDLES_ARG), argument(TRUFFLE_OBJECT, PTR)) // - .addBodyLine(call(null, "super", new String[]{JNI_HANDLES_ARG, PTR, STRUCT_SIZE}), ';'); + .withParams(argument(JNI_HANDLES_CLASS, JNI_HANDLES_ARG), argument(NATIVE_MEMORY_CLASS, NATIVE_MEMORY_ARG), argument(TRUFFLE_OBJECT, PTR)) // + .addBodyLine(call(null, "super", new String[]{JNI_HANDLES_ARG, NATIVE_MEMORY_ARG, PTR, STRUCT_SIZE}), ';'); wrapper.withMethod(wrapperConstructor); for (int i = 0; i < length; i++) { @@ -361,8 +364,8 @@ private static void generateWrapMethod(ClassBuilder struct, String wrapperClass) .withOverrideAnnotation() // .withModifiers(new ModifierBuilder().asPublic()) // .withReturnType(wrapperClass) // - .withParams(argument(JNI_HANDLES_CLASS, JNI_HANDLES_ARG), argument(TRUFFLE_OBJECT, PTR)) // - .addBodyLine("return new ", call(null, wrapperClass, new String[]{JNI_HANDLES_ARG, PTR}), ';'); + .withParams(argument(JNI_HANDLES_CLASS, JNI_HANDLES_ARG), argument(NATIVE_MEMORY_CLASS, NATIVE_MEMORY_ARG), argument(TRUFFLE_OBJECT, PTR)) // + .addBodyLine("return new ", call(null, wrapperClass, new String[]{JNI_HANDLES_ARG, NATIVE_MEMORY_ARG, PTR}), ';'); struct.withMethod(wrapMethod); } @@ -371,7 +374,7 @@ private static String generateStructCollector(List structs) { .withCopyright() // .inPackage(STRUCTS_PACKAGE) // .withImportGroup(Arrays.asList(IMPORT_INTEROP_LIBRARY, IMPORT_TRUFFLE_OBJECT)) // - .withImportGroup(Collections.singletonList(IMPORT_JNI_HANDLES)); + .withImportGroup(Arrays.asList(IMPORT_JNI_HANDLES, IMPORT_NATIVE_MEMORY)); ClassBuilder structCollector = new ClassBuilder(STRUCTS_CLASS) // .withQualifiers(new ModifierBuilder().asPublic().asFinal()); @@ -393,10 +396,11 @@ private static void generateCollectorConstructor(ClassBuilder collector, ListLifetime - * - * @return null if the memory cannot be allocated. Otherwise, a - * {@link InteropLibrary#hasBufferElements(Object) buffer}. - * @throws IllegalArgumentException if the size is negative - */ - default @Buffer TruffleObject allocateMemory(long size) { - long address = nativeMemory().allocateMemory(size); - if (address == 0) { - return null; - } - return RawPointer.create(address); - } - - /** - * Similar to realloc. The result of allocating a 0-sized buffer is an implementation detail. - * - * @return null if the memory cannot be re-allocated. Otherwise, a - * {@link InteropLibrary#hasBufferElements(Object) buffer}. - * @throws IllegalArgumentException if the size is negative - */ - default @Buffer TruffleObject reallocateMemory(@Pointer TruffleObject buffer, long newSize) { - long address = 0; - try { - address = InteropLibrary.getUncached().asPointer(buffer); - } catch (UnsupportedMessageException e) { - throw EspressoError.shouldNotReachHere(e); - } - long rawPointer = nativeMemory().reallocateMemory(address, newSize); - if (rawPointer == 0) { - return null; - } - return RawPointer.create(rawPointer); - } - - /** - * Similar to free. Accessing the buffer after free may cause explosive undefined behavior. - */ - default void freeMemory(@Pointer TruffleObject buffer) { - long address = 0; - try { - address = InteropLibrary.getUncached().asPointer(buffer); - } catch (UnsupportedMessageException e) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw EspressoError.shouldNotReachHere(e); - } - nativeMemory().freeMemory(address); - } - /** * Sinking, make a Java method accessible to the native world. Returns an * {@link InteropLibrary#isPointer(Object) pointer} {@link TruffleObject object}, callable from diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/RawPointer.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/RawPointer.java index 522c9bf66587..d8bcc96cfa87 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/RawPointer.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/RawPointer.java @@ -26,6 +26,7 @@ import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.library.ExportLibrary; import com.oracle.truffle.api.library.ExportMessage; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; @ExportLibrary(InteropLibrary.class) public final class RawPointer implements TruffleObject { @@ -54,6 +55,22 @@ boolean isPointer() { return true; } + /** + * There is no guarantee that the address actually corresponds to a host-native-address. It + * should correspond to one of the following: + *

    + *
  • A {@link NativeMemory} address
  • + *
  • A handle, such as in + * {@link com.oracle.truffle.espresso.vm.VM#JVM_LoadLibrary(String, boolean)}
  • + *
  • A special value, such as: + *
      + *
    • The NULL pointer in {@link RawPointer}
    • + *
    • The sentinelPointer in + * {@link com.oracle.truffle.espresso.libs.libjvm.impl.LibJVMSubstitutions}
    • + *
    + *
  • + *
+ */ @ExportMessage long asPointer() { return rawPtr; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/TruffleByteBuffer.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/TruffleByteBuffer.java index e09ec96e39e8..9b5c57b88916 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/TruffleByteBuffer.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/TruffleByteBuffer.java @@ -22,6 +22,8 @@ */ package com.oracle.truffle.espresso.ffi; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.MemoryAllocationException; + import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.ReadOnlyBufferException; @@ -39,104 +41,88 @@ import com.oracle.truffle.api.library.ExportLibrary; import com.oracle.truffle.api.library.ExportMessage; import com.oracle.truffle.api.profiles.BranchProfile; -import com.oracle.truffle.espresso.classfile.JavaKind; -import com.oracle.truffle.espresso.ffi.nfi.NativeUtils; +import com.oracle.truffle.espresso.ffi.memory.MemoryBuffer; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; @ExportLibrary(InteropLibrary.class) -public final class TruffleByteBuffer implements TruffleObject { - - private final ByteBuffer byteBuffer; - - private TruffleByteBuffer(@Pointer TruffleObject addressPtr, long byteCapacity) { - if (byteCapacity < 0) { - throw new IllegalArgumentException("negative requested capacity"); - } - this.byteBuffer = NativeUtils.directByteBuffer(NativeUtils.interopAsPointer(addressPtr), byteCapacity); - } +public final class TruffleByteBuffer implements TruffleObject, AutoCloseable { - private TruffleByteBuffer(ByteBuffer byteBuffer) { - this.byteBuffer = Objects.requireNonNull(byteBuffer); - } + private final MemoryBuffer byteBufferRecord; - public static @Buffer TruffleObject create(ByteBuffer byteBuffer) { - return new TruffleByteBuffer(byteBuffer); + private TruffleByteBuffer(MemoryBuffer memoryBuffer) { + this.byteBufferRecord = Objects.requireNonNull(memoryBuffer); } - public static @Buffer TruffleObject create(@Pointer TruffleObject addressPtr, long size, JavaKind kind) { - long byteCapacity = Math.multiplyExact(size, kind.getByteCount()); - return new TruffleByteBuffer(addressPtr, byteCapacity); + /** + * Creates a TruffleByteBuffer from the given MemoryBuffer. The returned TruffleByteBuffer must + * be explicitly closed to avoid leaking resources. + */ + public static TruffleByteBuffer create(MemoryBuffer memoryBuffer) { + return new TruffleByteBuffer(memoryBuffer); } + /** + * Creates a TruffleByteBuffer containing the UTF8 encoded bytes of the given string. The + * returned TruffleByteBuffer must be explicitly closed to avoid leaking resources. + * + * @param string the string to be buffered. + * @param nativeMemory the nativeMemory where the TruffleByteBuffer is allocated. + * @return a TruffleByteBuffer containing the UTF-8 bytes of the string + */ @TruffleBoundary - public static @Buffer TruffleObject allocateDirectStringUTF8(String string) { - return allocateDirectString(string, StandardCharsets.UTF_8); + public static TruffleByteBuffer allocateDirectStringUTF8(String string, NativeMemory nativeMemory) throws MemoryAllocationException { + return allocateDirectString(string, StandardCharsets.UTF_8, nativeMemory); } - @TruffleBoundary - public static @Buffer TruffleObject allocateDirectString(String string, Charset charset) { - byte[] bytes = string.getBytes(charset); - ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.length + 1); - buffer.put(bytes); - buffer.put((byte) 0); - return create(buffer); + @Override + public void close() { + this.byteBufferRecord.close(); } - /** - * Allocates a managed ByteBuffer, the returned TruffleBuffer is not an - * {@link InteropLibrary#isPointer(Object) interop pointer}. - * - *

- * The returned object is managed by the GC, along with its native resources and cannot be - * de-allocated explicitly. - * - * @param byteCapacity buffer size in bytes - * @throws IllegalArgumentException if the supplied capacity is negative - * @throws OutOfMemoryError if the buffer cannot be allocated - */ - public static @Buffer TruffleObject allocate(int byteCapacity) { - return create(ByteBuffer.allocate(byteCapacity)); + @SuppressWarnings("static-method") + @ExportMessage + boolean isPointer() { + return true; } /** - * Allocates a managed direct/native buffer, the returned TruffleBuffer is an - * {@link InteropLibrary#isPointer(Object) interop pointer}. + * Creates a TruffleByteBuffer containing bytes of the given string encoded with the charset. + * The returned TruffleByteBuffer must be explicitly closed to avoid leaking resources. * - *

- * The returned object is managed by the GC, along with its native resources and cannot be - * de-allocated explicitly. - * - * @param byteCapacity buffer size in bytes - * @throws IllegalArgumentException if the supplied capacity is negative - * @throws OutOfMemoryError if the buffer cannot be allocated + * @param string the string to be buffered. + * @param charset the charset used for encoding + * @param nativeMemory the nativeMemory where the TruffleByteBuffer is allocated. + * @return a TruffleByteBuffer containing the UTF-8 bytes of the string */ - public static @Buffer TruffleObject allocateDirect(int byteCapacity) { - return create(ByteBuffer.allocateDirect(byteCapacity)); + @TruffleBoundary + public static TruffleByteBuffer allocateDirectString(String string, Charset charset, NativeMemory nativeMemory) throws MemoryAllocationException { + byte[] bytes = string.getBytes(charset); + MemoryBuffer memoryBuffer = nativeMemory.allocateMemoryBuffer(bytes.length + 1); + ByteBuffer buffer = memoryBuffer.buffer(); + buffer.put(bytes); + buffer.put((byte) 0); + return create(memoryBuffer); } /** - * Creates an unmanaged, native TruffleBuffer wrapping a of native memory. The returned - * TruffleBuffer is an {@link InteropLibrary#isPointer(Object) interop pointer}. + * There is no guarantee that the address actually corresponds to a host-native-address. It + * should correspond to one of the following: + *

    + *
  • A {@link NativeMemory} address
  • + *
  • A handle, such as in + * {@link com.oracle.truffle.espresso.vm.VM#JVM_LoadLibrary(String, boolean)}
  • + *
  • A special value, such as: + *
      + *
    • The NULL pointer in {@link RawPointer}
    • + *
    • The sentinelPointer in + * {@link com.oracle.truffle.espresso.libs.libjvm.impl.LibJVMSubstitutions}
    • + *
    + *
  • + *
*/ - public static @Buffer TruffleObject wrap(@Pointer TruffleObject address, int byteCapacity) { - assert InteropLibrary.getUncached().isPointer(address); - return create(NativeUtils.directByteBuffer(address, byteCapacity)); - } - - public static @Buffer TruffleObject wrap(@Pointer TruffleObject addressPtr, int elemCount, JavaKind kind) { - return wrap(addressPtr, Math.multiplyExact(elemCount, kind.getByteCount())); - } - @ExportMessage - boolean isPointer() { - return this.byteBuffer.isDirect(); - } - - @ExportMessage - long asPointer() throws UnsupportedMessageException { - if (!isPointer()) { - throw UnsupportedMessageException.create(); - } - return NativeUtils.byteBufferAddress(this.byteBuffer); + long asPointer() { + return byteBufferRecord.address(); } @SuppressWarnings("static-method") @@ -147,20 +133,20 @@ boolean hasBufferElements() { @ExportMessage long getBufferSize() { - return byteBuffer.capacity(); + return byteBufferRecord.buffer().capacity(); } @SuppressWarnings("static-method") @ExportMessage boolean isBufferWritable() { - return !byteBuffer.isReadOnly(); + return !byteBufferRecord.buffer().isReadOnly(); } @ExportMessage byte readBufferByte(long byteOffset, @Shared("error") @Cached BranchProfile error) throws InvalidBufferOffsetException { try { int index = Math.toIntExact(byteOffset); - return readByte(this.byteBuffer, index); + return readByte(this.byteBufferRecord.buffer(), index); } catch (ArithmeticException | IndexOutOfBoundsException e) { error.enter(); throw InvalidBufferOffsetException.create(byteOffset, getBufferSize()); @@ -175,7 +161,7 @@ void readBuffer(long byteOffset, byte[] destination, int destinationOffset, int } try { int index = Math.toIntExact(byteOffset); - readBytes(this.byteBuffer, index, destination, destinationOffset, length); + readBytes(this.byteBufferRecord.buffer(), index, destination, destinationOffset, length); } catch (ArithmeticException | IndexOutOfBoundsException e) { error.enter(); throw InvalidBufferOffsetException.create(byteOffset, length); @@ -186,7 +172,7 @@ void readBuffer(long byteOffset, byte[] destination, int destinationOffset, int void writeBufferByte(long byteOffset, byte value, @Shared("error") @Cached BranchProfile error) throws UnsupportedMessageException, InvalidBufferOffsetException { try { int index = Math.toIntExact(byteOffset); - writeByte(this.byteBuffer, index, value); + writeByte(this.byteBufferRecord.buffer(), index, value); } catch (ArithmeticException | IndexOutOfBoundsException e) { error.enter(); throw InvalidBufferOffsetException.create(byteOffset, getBufferSize()); @@ -200,7 +186,7 @@ void writeBufferByte(long byteOffset, byte value, @Shared("error") @Cached Branc short readBufferShort(ByteOrder order, long byteOffset, @Shared("error") @Cached BranchProfile error) throws InvalidBufferOffsetException { try { int index = Math.toIntExact(byteOffset); - return readShort(this.byteBuffer, order, index); + return readShort(this.byteBufferRecord.buffer(), order, index); } catch (ArithmeticException | IndexOutOfBoundsException e) { error.enter(); throw InvalidBufferOffsetException.create(byteOffset, getBufferSize()); @@ -211,7 +197,7 @@ short readBufferShort(ByteOrder order, long byteOffset, @Shared("error") @Cached void writeBufferShort(ByteOrder order, long byteOffset, short value, @Shared("error") @Cached BranchProfile error) throws UnsupportedMessageException, InvalidBufferOffsetException { try { int index = Math.toIntExact(byteOffset); - writeShort(this.byteBuffer, order, index, value); + writeShort(this.byteBufferRecord.buffer(), order, index, value); } catch (ArithmeticException | IndexOutOfBoundsException e) { error.enter(); throw InvalidBufferOffsetException.create(byteOffset, getBufferSize()); @@ -225,7 +211,7 @@ void writeBufferShort(ByteOrder order, long byteOffset, short value, @Shared("er int readBufferInt(ByteOrder order, long byteOffset, @Shared("error") @Cached BranchProfile error) throws InvalidBufferOffsetException { try { int index = Math.toIntExact(byteOffset); - return readInt(this.byteBuffer, order, index); + return readInt(this.byteBufferRecord.buffer(), order, index); } catch (ArithmeticException | IndexOutOfBoundsException e) { error.enter(); throw InvalidBufferOffsetException.create(byteOffset, getBufferSize()); @@ -236,7 +222,7 @@ int readBufferInt(ByteOrder order, long byteOffset, @Shared("error") @Cached Bra void writeBufferInt(ByteOrder order, long byteOffset, int value, @Shared("error") @Cached BranchProfile error) throws UnsupportedMessageException, InvalidBufferOffsetException { try { int index = Math.toIntExact(byteOffset); - writeInt(this.byteBuffer, order, index, value); + writeInt(this.byteBufferRecord.buffer(), order, index, value); } catch (ArithmeticException | IndexOutOfBoundsException e) { error.enter(); throw InvalidBufferOffsetException.create(byteOffset, getBufferSize()); @@ -250,7 +236,7 @@ void writeBufferInt(ByteOrder order, long byteOffset, int value, @Shared("error" long readBufferLong(ByteOrder order, long byteOffset, @Shared("error") @Cached BranchProfile error) throws InvalidBufferOffsetException { try { int index = Math.toIntExact(byteOffset); - return readLong(this.byteBuffer, order, index); + return readLong(this.byteBufferRecord.buffer(), order, index); } catch (ArithmeticException | IndexOutOfBoundsException e) { error.enter(); throw InvalidBufferOffsetException.create(byteOffset, getBufferSize()); @@ -261,7 +247,7 @@ long readBufferLong(ByteOrder order, long byteOffset, @Shared("error") @Cached B void writeBufferLong(ByteOrder order, long byteOffset, long value, @Shared("error") @Cached BranchProfile error) throws UnsupportedMessageException, InvalidBufferOffsetException { try { int index = Math.toIntExact(byteOffset); - writeLong(this.byteBuffer, order, index, value); + writeLong(this.byteBufferRecord.buffer(), order, index, value); } catch (ArithmeticException | IndexOutOfBoundsException e) { error.enter(); throw InvalidBufferOffsetException.create(byteOffset, getBufferSize()); @@ -275,7 +261,7 @@ void writeBufferLong(ByteOrder order, long byteOffset, long value, @Shared("erro float readBufferFloat(ByteOrder order, long byteOffset, @Shared("error") @Cached BranchProfile error) throws InvalidBufferOffsetException { try { int index = Math.toIntExact(byteOffset); - return readFloat(this.byteBuffer, order, index); + return readFloat(this.byteBufferRecord.buffer(), order, index); } catch (ArithmeticException | IndexOutOfBoundsException e) { error.enter(); throw InvalidBufferOffsetException.create(byteOffset, getBufferSize()); @@ -286,7 +272,7 @@ float readBufferFloat(ByteOrder order, long byteOffset, @Shared("error") @Cached void writeBufferFloat(ByteOrder order, long byteOffset, float value, @Shared("error") @Cached BranchProfile error) throws UnsupportedMessageException, InvalidBufferOffsetException { try { int index = Math.toIntExact(byteOffset); - writeFloat(this.byteBuffer, order, index, value); + writeFloat(this.byteBufferRecord.buffer(), order, index, value); } catch (ArithmeticException | IndexOutOfBoundsException e) { error.enter(); throw InvalidBufferOffsetException.create(byteOffset, getBufferSize()); @@ -300,7 +286,7 @@ void writeBufferFloat(ByteOrder order, long byteOffset, float value, @Shared("er double readBufferDouble(ByteOrder order, long byteOffset, @Shared("error") @Cached BranchProfile error) throws InvalidBufferOffsetException { try { int index = Math.toIntExact(byteOffset); - return readDouble(this.byteBuffer, order, index); + return readDouble(this.byteBufferRecord.buffer(), order, index); } catch (ArithmeticException | IndexOutOfBoundsException e) { error.enter(); throw InvalidBufferOffsetException.create(byteOffset, getBufferSize()); @@ -311,7 +297,7 @@ void writeBufferFloat(ByteOrder order, long byteOffset, float value, @Shared("er void writeBufferDouble(ByteOrder order, long byteOffset, double value, @Shared("error") @Cached BranchProfile error) throws UnsupportedMessageException, InvalidBufferOffsetException { try { int index = Math.toIntExact(byteOffset); - writeDouble(this.byteBuffer, order, index, value); + writeDouble(this.byteBufferRecord.buffer(), order, index, value); } catch (ArithmeticException | IndexOutOfBoundsException e) { error.enter(); throw InvalidBufferOffsetException.create(byteOffset, getBufferSize()); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/nfi/NativeUtils.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/nfi/NativeUtils.java index f912232de1fd..aefe1068d06b 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/nfi/NativeUtils.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/nfi/NativeUtils.java @@ -22,8 +22,9 @@ */ package com.oracle.truffle.espresso.ffi.nfi; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.IllegalMemoryAccessException; + import java.nio.ByteBuffer; -import java.nio.ByteOrder; import java.nio.IntBuffer; import java.nio.LongBuffer; @@ -36,58 +37,84 @@ import com.oracle.truffle.espresso.classfile.descriptors.ModifiedUTF8; import com.oracle.truffle.espresso.ffi.Pointer; import com.oracle.truffle.espresso.ffi.RawPointer; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; import com.oracle.truffle.espresso.meta.EspressoError; -import com.oracle.truffle.espresso.vm.UnsafeAccess; - -import sun.misc.Unsafe; +import com.oracle.truffle.espresso.meta.Meta; +/** + * Utility class for performing low-level operations. + */ public final class NativeUtils { - - private static final Unsafe UNSAFE = UnsafeAccess.get(); - - private static final Class DIRECT_BYTE_BUFFER_CLASS; - private static final long ADDRESS_FIELD_OFFSET; - private static final long CAPACITY_FIELD_OFFSET; - - @SuppressWarnings("deprecation") - private static long getBufferFieldOffset(String name) throws NoSuchFieldException { - // TODO replace with panama? - return UNSAFE.objectFieldOffset(java.nio.Buffer.class.getDeclaredField(name)); + /** + * Returns a MemoryBuffer of the address represented by the given addressPtr with the capacity + * to hold size many elements of the kind specified by the argument. + * + * @param addressPtr the address pointer + * @param size How many elements the buffer can store. + * @param kind the kind of elements the buffer should store + * @param nativeMemory the NativeMemory implementation + * @return The MemoryBuffer of the specified memory region. + * @throws IllegalMemoryAccessException if {@code addressPtr} does not point to a valid memory + * region. + */ + public static ByteBuffer wrapNativeMemory(@Pointer TruffleObject addressPtr, long size, JavaKind kind, NativeMemory nativeMemory) throws IllegalMemoryAccessException { + return wrapNativeMemory(addressPtr, Math.multiplyExact(size, kind.getByteCount()), nativeMemory); + } + + /** + * Works as specified by + * {@link NativeUtils#wrapNativeMemory(TruffleObject, long, JavaKind, NativeMemory)} with the + * only difference that the returned {@link ByteBuffer} will hold exactly capacity many bytes. + */ + @TruffleBoundary + public static ByteBuffer wrapNativeMemory(@Pointer TruffleObject addressPtr, long capacity, NativeMemory nativeMemory) throws IllegalMemoryAccessException { + return wrapNativeMemory(interopAsPointer(addressPtr), capacity, nativeMemory); } - static { - try { - ADDRESS_FIELD_OFFSET = getBufferFieldOffset("address"); - CAPACITY_FIELD_OFFSET = getBufferFieldOffset("capacity"); - DIRECT_BYTE_BUFFER_CLASS = Class.forName("java.nio.DirectByteBuffer"); - } catch (ClassNotFoundException | NoSuchFieldException e) { - throw EspressoError.shouldNotReachHere(e); - } + /** + * Works as specified by + * {@link NativeUtils#wrapNativeMemory(TruffleObject, long, JavaKind, NativeMemory)} with the + * only difference that the returned {@link ByteBuffer} will hold exactly capacity many bytes. + */ + @TruffleBoundary + public static ByteBuffer wrapNativeMemory(long address, long longCapacity, NativeMemory nativeMemory) throws IllegalMemoryAccessException { + return nativeMemory.wrapNativeMemory(address, Math.toIntExact(longCapacity)); } - public static ByteBuffer directByteBuffer(@Pointer TruffleObject addressPtr, long size, JavaKind kind) { - return directByteBuffer(addressPtr, Math.multiplyExact(size, kind.getByteCount())); + /** + * Performs {@link NativeUtils#wrapNativeMemory(TruffleObject, long, JavaKind, NativeMemory)} + * but throws a guest {@link InternalError} if a host {@link IllegalMemoryAccessException} + * occurs. + */ + @TruffleBoundary + public static ByteBuffer wrapNativeMemoryOrThrow(@Pointer TruffleObject addressPtr, long size, JavaKind kind, NativeMemory nativeMemory, Meta meta) { + return wrapNativeMemoryOrThrow(addressPtr, Math.multiplyExact(size, kind.getByteCount()), nativeMemory, meta); } + /** + * Performs {@link NativeUtils#wrapNativeMemory(long, long, NativeMemory)} but throws a guest + * {@link InternalError} if a host {@link IllegalMemoryAccessException} occurs. + */ @TruffleBoundary - public static ByteBuffer directByteBuffer(@Pointer TruffleObject addressPtr, long capacity) { - return directByteBuffer(interopAsPointer(addressPtr), capacity); + public static ByteBuffer wrapNativeMemoryOrThrow(@Pointer TruffleObject addressPtr, long capacity, NativeMemory nativeMemory, Meta meta) { + try { + return wrapNativeMemory(addressPtr, capacity, nativeMemory); + } catch (IllegalMemoryAccessException e) { + throw meta.throwInternalErrorBoundary(e.toString()); + } } + /** + * Performs {@link NativeUtils#interopPointerToString(TruffleObject, NativeMemory)} but throws a + * guest {@link InternalError} if a host {@link IllegalMemoryAccessException} occurs. + */ @TruffleBoundary - public static ByteBuffer directByteBuffer(long address, long longCapacity) { - int capacity = Math.toIntExact(longCapacity); - ByteBuffer buffer = null; + public static String interopPointerToStringOrThrow(@Pointer TruffleObject interopPtr, NativeMemory nativeMemory, Meta meta) { try { - buffer = (ByteBuffer) UNSAFE.allocateInstance(DIRECT_BYTE_BUFFER_CLASS); - } catch (InstantiationException e) { - throw EspressoError.shouldNotReachHere(e); + return NativeUtils.interopPointerToString(interopPtr, nativeMemory); + } catch (IllegalMemoryAccessException e) { + throw meta.throwInternalErrorBoundary(e.toString()); } - UNSAFE.putLong(buffer, ADDRESS_FIELD_OFFSET, address); - UNSAFE.putInt(buffer, CAPACITY_FIELD_OFFSET, capacity); - buffer.clear(); - buffer.order(ByteOrder.nativeOrder()); - return buffer; } @TruffleBoundary @@ -100,87 +127,85 @@ public static long interopAsPointer(@Pointer TruffleObject interopPtr) { } } - public static String interopPointerToString(@Pointer TruffleObject interopPtr) { - return fromUTF8Ptr(interopAsPointer(interopPtr)); - } - - public static void writeToIntPointer(TruffleObject pointer, int value) { - writeToIntPointer(InteropLibrary.getUncached(), pointer, value); + /** + * Returns a String by decoding the memory region of interopPtr using UTF-8. + * + * @param interopPtr the interop pointer to decode + * @param nativeMemory the native memory implementation + * @return the decoded string + * @throws IllegalMemoryAccessException if {@code interopPtr} does not point to a valid memory + * region. + */ + public static String interopPointerToString(@Pointer TruffleObject interopPtr, NativeMemory nativeMemory) throws IllegalMemoryAccessException { + long rawBytesPtr = interopAsPointer(interopPtr); + if (rawBytesPtr == 0) { + return null; + } + ByteBuffer buf = wrapNativeMemory(rawBytesPtr, Integer.MAX_VALUE, nativeMemory); + while (buf.get() != 0) { + // find the end of the utf8 + } + buf.limit(buf.position() - 1); + buf.position(0); + return ModifiedUTF8.toJavaStringSafe(buf); } + /** + * Writes the provided int to the pointer. + * + * @throws IllegalMemoryAccessException if {@code pointer} does not point to a valid memory + * region. + */ @TruffleBoundary - public static void writeToIntPointer(InteropLibrary library, TruffleObject pointer, int value) { + public static void writeToIntPointer(InteropLibrary library, TruffleObject pointer, int value, NativeMemory nativeMemory) throws IllegalMemoryAccessException { if (library.isNull(pointer)) { throw new NullPointerException(); } - IntBuffer resultPointer = NativeUtils.directByteBuffer(pointer, 1, JavaKind.Int).asIntBuffer(); + IntBuffer resultPointer = NativeUtils.wrapNativeMemory(pointer, 1, JavaKind.Int, nativeMemory).asIntBuffer(); resultPointer.put(value); } - public static void writeToLongPointer(TruffleObject pointer, long value) { - writeToLongPointer(InteropLibrary.getUncached(), pointer, value); - } - + /** + * Writes the provided long to the pointer. + * + * @throws IllegalMemoryAccessException if {@code pointer} does not point to a valid memory + * region. + */ @TruffleBoundary - public static void writeToLongPointer(InteropLibrary library, TruffleObject pointer, long value) { + public static void writeToLongPointer(InteropLibrary library, TruffleObject pointer, long value, NativeMemory nativeMemory) throws IllegalMemoryAccessException { if (library.isNull(pointer)) { throw new NullPointerException(); } - LongBuffer resultPointer = NativeUtils.directByteBuffer(pointer, 1, JavaKind.Long).asLongBuffer(); + LongBuffer resultPointer = NativeUtils.wrapNativeMemory(pointer, 1, JavaKind.Long, nativeMemory).asLongBuffer(); resultPointer.put(value); } - public static void writeToPointerPointer(TruffleObject pointer, TruffleObject value) { - writeToPointerPointer(InteropLibrary.getUncached(), pointer, value); - } - - public static void writeToPointerPointer(InteropLibrary library, TruffleObject pointer, TruffleObject value) { - writeToLongPointer(library, pointer, NativeUtils.interopAsPointer(value)); - } - - public static TruffleObject dereferencePointerPointer(TruffleObject pointer) { - return dereferencePointerPointer(InteropLibrary.getUncached(), pointer); - } - + /** + * Writes the long value of the value pointer to the address given by the address pointer. + * + * @param library the InteropLibrary + * @param pointer the address pointer + * @param value the value provided as a pointer + * @param nativeMemory the NativeMemory implementation + * @throws IllegalMemoryAccessException if {@code pointer} or {@code value} does not point to a + * valid memory region. + */ + public static void writeToPointerPointer(InteropLibrary library, TruffleObject pointer, TruffleObject value, NativeMemory nativeMemory) throws IllegalMemoryAccessException { + writeToLongPointer(library, pointer, NativeUtils.interopAsPointer(value), nativeMemory); + } + + /** + * Dereferences the given pointer. + * + * @throws IllegalMemoryAccessException if {@code pointer} does not point to a valid memory + * region. + */ @TruffleBoundary - public static TruffleObject dereferencePointerPointer(InteropLibrary library, TruffleObject pointer) { + public static TruffleObject dereferencePointerPointer(InteropLibrary library, TruffleObject pointer, NativeMemory nativeMemory) throws IllegalMemoryAccessException { if (library.isNull(pointer)) { throw new NullPointerException(); } - LongBuffer buffer = NativeUtils.directByteBuffer(pointer, 1, JavaKind.Long).asLongBuffer(); + LongBuffer buffer = NativeUtils.wrapNativeMemory(pointer, 1, JavaKind.Long, nativeMemory).asLongBuffer(); return RawPointer.create(buffer.get()); } - - @TruffleBoundary - public static long byteBufferAddress(ByteBuffer byteBuffer) { - assert byteBuffer.isDirect(); - return UNSAFE.getLong(byteBuffer, ADDRESS_FIELD_OFFSET); - } - - public static @Pointer TruffleObject byteBufferPointer(ByteBuffer byteBuffer) { - return new RawPointer(byteBufferAddress(byteBuffer)); - } - - public static String fromUTF8Ptr(@Pointer TruffleObject buffPtr) { - return fromUTF8Ptr(interopAsPointer(buffPtr)); - } - - @TruffleBoundary - public static String fromUTF8Ptr(long rawBytesPtr) { - if (rawBytesPtr == 0) { - return null; - } - ByteBuffer buf = directByteBuffer(rawBytesPtr, Integer.MAX_VALUE); - while (buf.get() != 0) { - // find the end of the utf8 - } - buf.limit(buf.position() - 1); - buf.position(0); - return ModifiedUTF8.toJavaStringSafe(buf); - } - - @TruffleBoundary - public static ByteBuffer allocateDirect(int capacity) { - return ByteBuffer.allocateDirect(capacity).order(ByteOrder.nativeOrder()); - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jni/JniEnv.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jni/JniEnv.java index 3e38dcac05d2..1ec0630ed098 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jni/JniEnv.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jni/JniEnv.java @@ -22,11 +22,13 @@ */ package com.oracle.truffle.espresso.jni; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.IllegalMemoryAccessException; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.MemoryAllocationException; + import java.io.PrintWriter; import java.lang.reflect.Executable; import java.nio.Buffer; import java.nio.ByteBuffer; -import java.nio.ByteOrder; import java.nio.CharBuffer; import java.nio.DoubleBuffer; import java.nio.FloatBuffer; @@ -65,6 +67,8 @@ import com.oracle.truffle.espresso.ffi.NativeType; import com.oracle.truffle.espresso.ffi.Pointer; import com.oracle.truffle.espresso.ffi.RawPointer; +import com.oracle.truffle.espresso.ffi.memory.MemoryBuffer; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; import com.oracle.truffle.espresso.ffi.nfi.NativeUtils; import com.oracle.truffle.espresso.impl.ArrayKlass; import com.oracle.truffle.espresso.impl.Field; @@ -149,6 +153,7 @@ public final class JniEnv extends NativeEnv { private final JniVersion jniVersion; private final String debugEnvName; + private final NativeMemory nativeMemory; // Native library nespresso.dll (Windows) or libnespresso.so (Unixes) at runtime. private final TruffleObject nespressoLibrary; @@ -191,6 +196,7 @@ private JniEnv(EspressoContext context, NativeAccess nativeAccess) { popObject = nativeAccess.lookupAndBindSymbol(nespressoLibrary, "pop_object", NativeSignature.create(NativeType.OBJECT, NativeType.POINTER)); jniEnvPtr = initializeAndGetEnv(initializeNativeContext); + nativeMemory = context.getNativeAccess().nativeMemory(); assert jniEnvPtr != null; assert !getUncached().isNull(jniEnvPtr) || !getLanguage().isNativeAvailable(); assert getUncached().isPointer(jniEnvPtr); @@ -366,16 +372,20 @@ protected String getName() { } @TruffleBoundary - private ByteBuffer allocateDirect(int capacity, JavaKind kind) { + private MemoryBuffer allocateDirect(int capacity, JavaKind kind) { return allocateDirect(Math.multiplyExact(capacity, kind.getByteCount())); } @TruffleBoundary - private ByteBuffer allocateDirect(int capacity) { - ByteBuffer bb = ByteBuffer.allocateDirect(capacity).order(ByteOrder.nativeOrder()); - long address = NativeUtils.byteBufferAddress(bb); - nativeBuffers.put(address, bb); - return bb; + private MemoryBuffer allocateDirect(int capacity) { + try { + MemoryBuffer memoryBuffer = nativeMemory.allocateMemoryBuffer(capacity); + nativeBuffers.put(memoryBuffer.address(), memoryBuffer.buffer()); + return memoryBuffer; + } catch (MemoryAllocationException e) { + Meta meta = getContext().getMeta(); + throw meta.throwExceptionWithMessage(meta.java_lang_OutOfMemoryError, e.getMessage(), getContext()); + } } public static JniEnv create(EspressoContext context) { @@ -433,12 +443,14 @@ private WeakHandles methodIds() { * @throws NoSuchFieldError: if the specified field cannot be found. * @throws ExceptionInInitializerError: if the class initializer fails due to an exception. * @throws OutOfMemoryError: if the system runs out of memory. + * @throws InternalError: if {@code namePtr} or {@code typePtr} do not point to valid memory + * regions. */ @JniImpl @TruffleBoundary public @Handle(Field.class) long GetFieldID(@JavaType(Class.class) StaticObject clazz, @Pointer TruffleObject namePtr, @Pointer TruffleObject typePtr) { - String name = NativeUtils.interopPointerToString(namePtr); - String type = NativeUtils.interopPointerToString(typePtr); + String name = NativeUtils.interopPointerToStringOrThrow(namePtr, nativeMemory, getMeta()); + String type = NativeUtils.interopPointerToStringOrThrow(typePtr, nativeMemory, getMeta()); assert name != null && type != null; Klass klass = clazz.getMirrorKlass(getMeta()); @@ -478,12 +490,14 @@ private WeakHandles methodIds() { * @throws NoSuchFieldError if the specified static field cannot be found. * @throws ExceptionInInitializerError if the class initializer fails due to an exception. * @throws OutOfMemoryError if the system runs out of memory. + * @throws InternalError: if {@code namePtr} or {@code typePtr} do not point to valid memory + * regions. */ @JniImpl @TruffleBoundary public @Handle(Field.class) long GetStaticFieldID(@JavaType(Class.class) StaticObject clazz, @Pointer TruffleObject namePtr, @Pointer TruffleObject typePtr) { - String name = NativeUtils.interopPointerToString(namePtr); - String type = NativeUtils.interopPointerToString(typePtr); + String name = NativeUtils.interopPointerToStringOrThrow(namePtr, nativeMemory, getMeta()); + String type = NativeUtils.interopPointerToStringOrThrow(typePtr, nativeMemory, getMeta()); assert name != null && type != null; Field field = null; Symbol fieldName = getNames().lookup(name); @@ -523,12 +537,14 @@ private WeakHandles methodIds() { * @throws NoSuchMethodError if the specified method cannot be found. * @throws ExceptionInInitializerError if the class initializer fails due to an exception. * @throws OutOfMemoryError if the system runs out of memory. + * @throws InternalError: if {@code namePtr} or {@code signaturePtr} do not point to valid + * memory regions. */ @JniImpl @TruffleBoundary public @Handle(Method.class) long GetMethodID(@JavaType(Class.class) StaticObject clazz, @Pointer TruffleObject namePtr, @Pointer TruffleObject signaturePtr) { - String name = NativeUtils.interopPointerToString(namePtr); - String signature = NativeUtils.interopPointerToString(signaturePtr); + String name = NativeUtils.interopPointerToStringOrThrow(namePtr, nativeMemory, getMeta()); + String signature = NativeUtils.interopPointerToStringOrThrow(signaturePtr, nativeMemory, getMeta()); assert name != null && signature != null; Method method = null; Symbol methodName = getNames().lookup(name); @@ -564,12 +580,14 @@ private WeakHandles methodIds() { * @throws NoSuchMethodError if the specified static method cannot be found. * * @throws ExceptionInInitializerError if the class initializer fails due to an exception. * @throws OutOfMemoryError if the system runs out of memory. + * @throws InternalError: if {@code namePtr} or {@code signaturePtr} do not point to valid + * memory regions. */ @JniImpl @TruffleBoundary public @Handle(Method.class) long GetStaticMethodID(@JavaType(Class.class) StaticObject clazz, @Pointer TruffleObject namePtr, @Pointer TruffleObject signaturePtr) { - String name = NativeUtils.interopPointerToString(namePtr); - String signature = NativeUtils.interopPointerToString(signaturePtr); + String name = NativeUtils.interopPointerToStringOrThrow(namePtr, nativeMemory, getMeta()); + String signature = NativeUtils.interopPointerToStringOrThrow(signaturePtr, nativeMemory, getMeta()); assert name != null && signature != null; Method method = null; Symbol methodName = getNames().lookup(name); @@ -1216,7 +1234,7 @@ public void CallStaticVoidMethodVarargs(@JavaType(Class.class) StaticObject claz @JniImpl @TruffleBoundary public void GetBooleanArrayRegion(@JavaType(boolean[].class) StaticObject array, int start, int len, @Pointer TruffleObject bufPtr, @Inject EspressoLanguage language) { - ByteBuffer buf = NativeUtils.directByteBuffer(bufPtr, len, JavaKind.Byte); + ByteBuffer buf = NativeUtils.wrapNativeMemoryOrThrow(bufPtr, len, JavaKind.Byte, nativeMemory, getMeta()); if (array.isEspressoObject()) { byte[] contents = array.unwrap(language); boundsCheck(start, len, contents.length); @@ -1233,7 +1251,7 @@ public void GetBooleanArrayRegion(@JavaType(boolean[].class) StaticObject array, @JniImpl @TruffleBoundary public void GetCharArrayRegion(@JavaType(char[].class /* or byte[].class */) StaticObject array, int start, int len, @Pointer TruffleObject bufPtr, @Inject EspressoLanguage language) { - CharBuffer buf = NativeUtils.directByteBuffer(bufPtr, len, JavaKind.Char).asCharBuffer(); + CharBuffer buf = NativeUtils.wrapNativeMemoryOrThrow(bufPtr, len, JavaKind.Char, nativeMemory, getMeta()).asCharBuffer(); if (array.isEspressoObject()) { char[] contents = array.unwrap(language); boundsCheck(start, len, contents.length); @@ -1250,7 +1268,7 @@ public void GetCharArrayRegion(@JavaType(char[].class /* or byte[].class */) Sta @JniImpl @TruffleBoundary public void GetByteArrayRegion(@JavaType(byte[].class) StaticObject array, int start, int len, @Pointer TruffleObject bufPtr, @Inject EspressoLanguage language) { - ByteBuffer buf = NativeUtils.directByteBuffer(bufPtr, len, JavaKind.Byte); + ByteBuffer buf = NativeUtils.wrapNativeMemoryOrThrow(bufPtr, len, JavaKind.Byte, nativeMemory, getMeta()); if (array.isEspressoObject()) { byte[] contents = array.unwrap(language); boundsCheck(start, len, contents.length); @@ -1267,7 +1285,7 @@ public void GetByteArrayRegion(@JavaType(byte[].class) StaticObject array, int s @JniImpl @TruffleBoundary public void GetShortArrayRegion(@JavaType(short[].class) StaticObject array, int start, int len, @Pointer TruffleObject bufPtr, @Inject EspressoLanguage language) { - ShortBuffer buf = NativeUtils.directByteBuffer(bufPtr, len, JavaKind.Short).asShortBuffer(); + ShortBuffer buf = NativeUtils.wrapNativeMemoryOrThrow(bufPtr, len, JavaKind.Short, nativeMemory, getMeta()).asShortBuffer(); if (array.isEspressoObject()) { short[] contents = array.unwrap(language); boundsCheck(start, len, contents.length); @@ -1284,7 +1302,7 @@ public void GetShortArrayRegion(@JavaType(short[].class) StaticObject array, int @JniImpl @TruffleBoundary public void GetIntArrayRegion(@JavaType(int[].class) StaticObject array, int start, int len, @Pointer TruffleObject bufPtr, @Inject EspressoLanguage language) { - IntBuffer buf = NativeUtils.directByteBuffer(bufPtr, len, JavaKind.Int).asIntBuffer(); + IntBuffer buf = NativeUtils.wrapNativeMemoryOrThrow(bufPtr, len, JavaKind.Int, nativeMemory, getMeta()).asIntBuffer(); if (array.isEspressoObject()) { int[] contents = array.unwrap(language); boundsCheck(start, len, contents.length); @@ -1301,7 +1319,7 @@ public void GetIntArrayRegion(@JavaType(int[].class) StaticObject array, int sta @JniImpl @TruffleBoundary public void GetFloatArrayRegion(@JavaType(float[].class) StaticObject array, int start, int len, @Pointer TruffleObject bufPtr, @Inject EspressoLanguage language) { - FloatBuffer buf = NativeUtils.directByteBuffer(bufPtr, len, JavaKind.Float).asFloatBuffer(); + FloatBuffer buf = NativeUtils.wrapNativeMemoryOrThrow(bufPtr, len, JavaKind.Float, nativeMemory, getMeta()).asFloatBuffer(); if (array.isEspressoObject()) { float[] contents = array.unwrap(language); boundsCheck(start, len, contents.length); @@ -1318,7 +1336,7 @@ public void GetFloatArrayRegion(@JavaType(float[].class) StaticObject array, int @JniImpl @TruffleBoundary public void GetDoubleArrayRegion(@JavaType(double[].class) StaticObject array, int start, int len, @Pointer TruffleObject bufPtr, @Inject EspressoLanguage language) { - DoubleBuffer buf = NativeUtils.directByteBuffer(bufPtr, len, JavaKind.Double).asDoubleBuffer(); + DoubleBuffer buf = NativeUtils.wrapNativeMemoryOrThrow(bufPtr, len, JavaKind.Double, nativeMemory, getMeta()).asDoubleBuffer(); if (array.isEspressoObject()) { double[] contents = array.unwrap(language); boundsCheck(start, len, contents.length); @@ -1335,7 +1353,7 @@ public void GetDoubleArrayRegion(@JavaType(double[].class) StaticObject array, i @JniImpl @TruffleBoundary public void GetLongArrayRegion(@JavaType(long[].class) StaticObject array, int start, int len, @Pointer TruffleObject bufPtr, @Inject EspressoLanguage language) { - LongBuffer buf = NativeUtils.directByteBuffer(bufPtr, len, JavaKind.Long).asLongBuffer(); + LongBuffer buf = NativeUtils.wrapNativeMemoryOrThrow(bufPtr, len, JavaKind.Long, nativeMemory, getMeta()).asLongBuffer(); if (array.isEspressoObject()) { long[] contents = array.unwrap(language); boundsCheck(start, len, contents.length); @@ -1369,7 +1387,7 @@ private void checkForeignBounds(StaticObject array, int start, int len) { @JniImpl @TruffleBoundary public void SetBooleanArrayRegion(@JavaType(boolean[].class) StaticObject array, int start, int len, @Pointer TruffleObject bufPtr, @Inject EspressoLanguage language) { - ByteBuffer buf = NativeUtils.directByteBuffer(bufPtr, len, JavaKind.Byte); + ByteBuffer buf = NativeUtils.wrapNativeMemoryOrThrow(bufPtr, len, JavaKind.Byte, nativeMemory, getMeta()); if (array.isEspressoObject()) { byte[] contents = array.unwrap(language); boundsCheck(start, len, contents.length); @@ -1386,7 +1404,7 @@ public void SetBooleanArrayRegion(@JavaType(boolean[].class) StaticObject array, @JniImpl @TruffleBoundary public void SetCharArrayRegion(@JavaType(char[].class) StaticObject array, int start, int len, @Pointer TruffleObject bufPtr, @Inject EspressoLanguage language) { - CharBuffer buf = NativeUtils.directByteBuffer(bufPtr, len, JavaKind.Char).asCharBuffer(); + CharBuffer buf = NativeUtils.wrapNativeMemoryOrThrow(bufPtr, len, JavaKind.Char, nativeMemory, getMeta()).asCharBuffer(); if (array.isEspressoObject()) { char[] contents = array.unwrap(language); boundsCheck(start, len, contents.length); @@ -1403,7 +1421,7 @@ public void SetCharArrayRegion(@JavaType(char[].class) StaticObject array, int s @JniImpl @TruffleBoundary public void SetByteArrayRegion(@JavaType(byte[].class) StaticObject array, int start, int len, @Pointer TruffleObject bufPtr, @Inject EspressoLanguage language) { - ByteBuffer buf = NativeUtils.directByteBuffer(bufPtr, len, JavaKind.Byte); + ByteBuffer buf = NativeUtils.wrapNativeMemoryOrThrow(bufPtr, len, JavaKind.Byte, nativeMemory, getMeta()); if (array.isEspressoObject()) { byte[] contents = array.unwrap(language); boundsCheck(start, len, contents.length); @@ -1420,7 +1438,7 @@ public void SetByteArrayRegion(@JavaType(byte[].class) StaticObject array, int s @JniImpl @TruffleBoundary public void SetShortArrayRegion(@JavaType(short[].class) StaticObject array, int start, int len, @Pointer TruffleObject bufPtr, @Inject EspressoLanguage language) { - ShortBuffer buf = NativeUtils.directByteBuffer(bufPtr, len, JavaKind.Short).asShortBuffer(); + ShortBuffer buf = NativeUtils.wrapNativeMemoryOrThrow(bufPtr, len, JavaKind.Short, nativeMemory, getMeta()).asShortBuffer(); if (array.isEspressoObject()) { short[] contents = array.unwrap(language); boundsCheck(start, len, contents.length); @@ -1437,7 +1455,7 @@ public void SetShortArrayRegion(@JavaType(short[].class) StaticObject array, int @JniImpl @TruffleBoundary public void SetIntArrayRegion(@JavaType(int[].class) StaticObject array, int start, int len, @Pointer TruffleObject bufPtr, @Inject EspressoLanguage language) { - IntBuffer buf = NativeUtils.directByteBuffer(bufPtr, len, JavaKind.Int).asIntBuffer(); + IntBuffer buf = NativeUtils.wrapNativeMemoryOrThrow(bufPtr, len, JavaKind.Int, nativeMemory, getMeta()).asIntBuffer(); if (array.isEspressoObject()) { int[] contents = array.unwrap(language); boundsCheck(start, len, contents.length); @@ -1454,7 +1472,7 @@ public void SetIntArrayRegion(@JavaType(int[].class) StaticObject array, int sta @JniImpl @TruffleBoundary public void SetFloatArrayRegion(@JavaType(float[].class) StaticObject array, int start, int len, @Pointer TruffleObject bufPtr, @Inject EspressoLanguage language) { - FloatBuffer buf = NativeUtils.directByteBuffer(bufPtr, len, JavaKind.Float).asFloatBuffer(); + FloatBuffer buf = NativeUtils.wrapNativeMemoryOrThrow(bufPtr, len, JavaKind.Float, nativeMemory, getMeta()).asFloatBuffer(); if (array.isEspressoObject()) { float[] contents = array.unwrap(language); boundsCheck(start, len, contents.length); @@ -1471,7 +1489,7 @@ public void SetFloatArrayRegion(@JavaType(float[].class) StaticObject array, int @JniImpl @TruffleBoundary public void SetDoubleArrayRegion(@JavaType(double[].class) StaticObject array, int start, int len, @Pointer TruffleObject bufPtr, @Inject EspressoLanguage language) { - DoubleBuffer buf = NativeUtils.directByteBuffer(bufPtr, len, JavaKind.Double).asDoubleBuffer(); + DoubleBuffer buf = NativeUtils.wrapNativeMemoryOrThrow(bufPtr, len, JavaKind.Double, nativeMemory, getMeta()).asDoubleBuffer(); if (array.isEspressoObject()) { double[] contents = array.unwrap(language); boundsCheck(start, len, contents.length); @@ -1488,7 +1506,7 @@ public void SetDoubleArrayRegion(@JavaType(double[].class) StaticObject array, i @JniImpl @TruffleBoundary public void SetLongArrayRegion(@JavaType(long[].class) StaticObject array, int start, int len, @Pointer TruffleObject bufPtr, @Inject EspressoLanguage language) { - LongBuffer buf = NativeUtils.directByteBuffer(bufPtr, len, JavaKind.Long).asLongBuffer(); + LongBuffer buf = NativeUtils.wrapNativeMemoryOrThrow(bufPtr, len, JavaKind.Long, nativeMemory, getMeta()).asLongBuffer(); if (array.isEspressoObject()) { long[] contents = array.unwrap(language); boundsCheck(start, len, contents.length); @@ -1531,10 +1549,11 @@ public int GetStringLength(@JavaType(String.class) StaticObject string) { * @param bytesPtr pointer to a modified UTF-8 string. * @return a Java string object, or NULL if the string cannot be constructed. * @throws OutOfMemoryError if the system runs out of memory. + * @throws InternalError: if {@code bytesPtr} does not point to valid memory regions. */ @JniImpl public @JavaType(String.class) StaticObject NewStringUTF(@Pointer TruffleObject bytesPtr) { - String hostString = NativeUtils.fromUTF8Ptr(bytesPtr); + String hostString = NativeUtils.interopPointerToStringOrThrow(bytesPtr, nativeMemory, getMeta()); return getMeta().toGuestString(hostString); } @@ -1558,7 +1577,7 @@ public int GetStringLength(@JavaType(String.class) StaticObject string) { @TruffleBoundary public @Pointer TruffleObject GetStringCritical(@JavaType(String.class) StaticObject str, @Pointer TruffleObject isCopyPtr, @Inject EspressoLanguage language, @Inject Meta meta) { if (!getUncached().isNull(isCopyPtr)) { - ByteBuffer isCopyBuf = NativeUtils.directByteBuffer(isCopyPtr, 1); + ByteBuffer isCopyBuf = NativeUtils.wrapNativeMemoryOrThrow(isCopyPtr, 1, nativeMemory, getMeta()); isCopyBuf.put((byte) 1); // always copy since pinning is not supported } StaticObject stringChars; @@ -1568,10 +1587,10 @@ public int GetStringLength(@JavaType(String.class) StaticObject string) { stringChars = meta.java_lang_String_value.getObject(str); } int len = stringChars.length(language); - ByteBuffer criticalRegion = allocateDirect(len, JavaKind.Char); // direct byte buffer + MemoryBuffer criticalRegion = allocateDirect(len, JavaKind.Char); // (non-relocatable) @Pointer - TruffleObject address = NativeUtils.byteBufferPointer(criticalRegion); + TruffleObject address = new RawPointer(criticalRegion.address()); GetCharArrayRegion(stringChars, 0, len, address, language); return address; } @@ -1580,13 +1599,13 @@ public int GetStringLength(@JavaType(String.class) StaticObject string) { @TruffleBoundary public @Pointer TruffleObject GetStringUTFChars(@JavaType(String.class) StaticObject str, @Pointer TruffleObject isCopyPtr) { if (!getUncached().isNull(isCopyPtr)) { - ByteBuffer isCopyBuf = NativeUtils.directByteBuffer(isCopyPtr, 1); + ByteBuffer isCopyBuf = NativeUtils.wrapNativeMemoryOrThrow(isCopyPtr, 1, nativeMemory, getMeta()); isCopyBuf.put((byte) 1); // always copy since pinning is not supported } byte[] bytes = ModifiedUTF8.fromJavaString(getMeta().toHostString(str), true); - ByteBuffer region = allocateDirect(bytes.length); - region.put(bytes); - return NativeUtils.byteBufferPointer(region); + MemoryBuffer memoryBuffer = allocateDirect(bytes.length); + memoryBuffer.buffer().put(bytes); + return new RawPointer(memoryBuffer.address()); } /** @@ -1606,7 +1625,7 @@ public int GetStringLength(@JavaType(String.class) StaticObject string) { @TruffleBoundary public @Pointer TruffleObject GetStringChars(@JavaType(String.class) StaticObject string, @Pointer TruffleObject isCopyPtr, @Inject EspressoLanguage language) { if (!getUncached().isNull(isCopyPtr)) { - ByteBuffer isCopyBuf = NativeUtils.directByteBuffer(isCopyPtr, 1); + ByteBuffer isCopyBuf = NativeUtils.wrapNativeMemoryOrThrow(isCopyPtr, 1, nativeMemory, getMeta()); isCopyBuf.put((byte) 1); // always copy since pinning is not supported } char[] chars; @@ -1617,11 +1636,11 @@ public int GetStringLength(@JavaType(String.class) StaticObject string) { chars = getMeta().java_lang_String_value.getObject(string).unwrap(language); } // Add one for zero termination. - ByteBuffer bb = allocateDirect(chars.length + 1, JavaKind.Char); - CharBuffer region = bb.asCharBuffer(); + MemoryBuffer memoryBuffer = allocateDirect(chars.length + 1, JavaKind.Char); + CharBuffer region = memoryBuffer.buffer().asCharBuffer(); region.put(chars); region.put((char) 0); - return NativeUtils.byteBufferPointer(bb); + return new RawPointer(memoryBuffer.address()); } @TruffleBoundary @@ -1629,6 +1648,11 @@ public void releasePtr(@Pointer TruffleObject ptr) { long nativePtr = NativeUtils.interopAsPointer(ptr); assert nativeBuffers.containsKey(nativePtr); nativeBuffers.remove(nativePtr); + try { + getNativeAccess().nativeMemory().freeMemory(nativePtr); + } catch (IllegalMemoryAccessException e) { + throw getMeta().throwInternalErrorBoundary(e.toString()); + } } /** @@ -1685,7 +1709,7 @@ public void GetStringRegion(@JavaType(String.class) StaticObject str, int start, Meta meta = getMeta(); throw meta.throwException(meta.java_lang_StringIndexOutOfBoundsException); } - CharBuffer buf = NativeUtils.directByteBuffer(bufPtr, len, JavaKind.Char).asCharBuffer(); + CharBuffer buf = NativeUtils.wrapNativeMemoryOrThrow(bufPtr, len, JavaKind.Char, nativeMemory, getMeta()).asCharBuffer(); buf.put(chars, start, len); } @@ -1704,7 +1728,7 @@ public void GetStringUTFRegion(@JavaType(String.class) StaticObject str, int sta } // always 0-terminated. byte[] bytes = ModifiedUTF8.fromJavaString(hostString, start, len, true); - ByteBuffer buf = NativeUtils.directByteBuffer(bufPtr, bytes.length, JavaKind.Byte); + ByteBuffer buf = NativeUtils.wrapNativeMemoryOrThrow(bufPtr, bytes.length, JavaKind.Byte, nativeMemory, getMeta()); buf.put(bytes); } @@ -1768,11 +1792,17 @@ public static int Throw(@JavaType(Throwable.class) StaticObject obj, @Inject Met * @throws EspressoException the newly constructed {@link Throwable} object. */ @JniImpl + @TruffleBoundary public int ThrowNew(@JavaType(Class.class) StaticObject clazz, @Pointer TruffleObject messagePtr, @Inject Meta meta) { - String message = NativeUtils.interopPointerToString(messagePtr); - // The TLS exception slot will be set by the JNI wrapper. - // Throwing methods always return the default value, in this case 0 (success). - throw meta.throwExceptionWithMessage((ObjectKlass) clazz.getMirrorKlass(getMeta()), message); + try { + String message = NativeUtils.interopPointerToString(messagePtr, nativeMemory); + // The TLS exception slot will be set by the JNI wrapper. + // Throwing methods always return the default value, in this case 0 (success). + throw meta.throwExceptionWithMessage((ObjectKlass) clazz.getMirrorKlass(getMeta()), message); + } catch (IllegalMemoryAccessException e) { + getLanguage().setPendingException(meta.createInternalError(e.toString())); + return -1; + } } /** @@ -1822,7 +1852,12 @@ public void ExceptionDescribe(@Inject EspressoLanguage language) { @JniImpl @TruffleBoundary public void FatalError(@Pointer TruffleObject msgPtr, @Inject SubstitutionProfiler profiler) { - String msg = NativeUtils.interopPointerToString(msgPtr); + String msg; + try { + msg = NativeUtils.interopPointerToString(msgPtr, nativeMemory); + } catch (IllegalMemoryAccessException e) { + msg = "Error while retrieving error message: " + e; + } PrintWriter writer = new PrintWriter(getContext().err(), true); writer.println("FATAL ERROR in native method: " + msg); // TODO print stack trace @@ -1897,112 +1932,112 @@ public void SetObjectArrayElement(@JavaType(Object[].class) StaticObject array, @TruffleBoundary public @Pointer TruffleObject GetBooleanArrayElements(@JavaType(boolean[].class) StaticObject array, @Pointer TruffleObject isCopyPtr, @Inject EspressoLanguage language) { if (!getUncached().isNull(isCopyPtr)) { - ByteBuffer isCopyBuf = NativeUtils.directByteBuffer(isCopyPtr, 1); + ByteBuffer isCopyBuf = NativeUtils.wrapNativeMemoryOrThrow(isCopyPtr, 1, nativeMemory, getMeta()); isCopyBuf.put((byte) 1); // Always copy since pinning is not supported. } byte[] data = array.unwrap(language); - ByteBuffer bytes = allocateDirect(data.length, JavaKind.Boolean); - ByteBuffer elements = bytes; + MemoryBuffer memoryBuffer = allocateDirect(data.length, JavaKind.Boolean); + ByteBuffer elements = memoryBuffer.buffer(); elements.put(data); - return NativeUtils.byteBufferPointer(bytes); + return new RawPointer(memoryBuffer.address()); } @JniImpl @TruffleBoundary public @Pointer TruffleObject GetCharArrayElements(@JavaType(char[].class) StaticObject array, @Pointer TruffleObject isCopyPtr, @Inject EspressoLanguage language) { if (!getUncached().isNull(isCopyPtr)) { - ByteBuffer isCopyBuf = NativeUtils.directByteBuffer(isCopyPtr, 1); + ByteBuffer isCopyBuf = NativeUtils.wrapNativeMemoryOrThrow(isCopyPtr, 1, nativeMemory, getMeta()); isCopyBuf.put((byte) 1); // Always copy since pinning is not supported. } char[] data = array.unwrap(language); - ByteBuffer bytes = allocateDirect(data.length, JavaKind.Char); - CharBuffer elements = bytes.asCharBuffer(); + MemoryBuffer memoryBuffer = allocateDirect(data.length, JavaKind.Char); + CharBuffer elements = memoryBuffer.buffer().asCharBuffer(); elements.put(data); - return NativeUtils.byteBufferPointer(bytes); + return new RawPointer(memoryBuffer.address()); } @JniImpl @TruffleBoundary public @Pointer TruffleObject GetByteArrayElements(@JavaType(byte[].class) StaticObject array, @Pointer TruffleObject isCopyPtr, @Inject EspressoLanguage language) { if (!getUncached().isNull(isCopyPtr)) { - ByteBuffer isCopyBuf = NativeUtils.directByteBuffer(isCopyPtr, 1); + ByteBuffer isCopyBuf = NativeUtils.wrapNativeMemoryOrThrow(isCopyPtr, 1, nativeMemory, getMeta()); isCopyBuf.put((byte) 1); // Always copy since pinning is not supported. } byte[] data = array.unwrap(language); - ByteBuffer bytes = allocateDirect(data.length, JavaKind.Byte); - ByteBuffer elements = bytes; + MemoryBuffer memoryBuffer = allocateDirect(data.length, JavaKind.Byte); + ByteBuffer elements = memoryBuffer.buffer(); elements.put(data); - return NativeUtils.byteBufferPointer(bytes); + return new RawPointer(memoryBuffer.address()); } @JniImpl @TruffleBoundary public @Pointer TruffleObject GetShortArrayElements(@JavaType(short[].class) StaticObject array, @Pointer TruffleObject isCopyPtr, @Inject EspressoLanguage language) { if (!getUncached().isNull(isCopyPtr)) { - ByteBuffer isCopyBuf = NativeUtils.directByteBuffer(isCopyPtr, 1); + ByteBuffer isCopyBuf = NativeUtils.wrapNativeMemoryOrThrow(isCopyPtr, 1, nativeMemory, getMeta()); isCopyBuf.put((byte) 1); // Always copy since pinning is not supported. } short[] data = array.unwrap(language); - ByteBuffer bytes = allocateDirect(data.length, JavaKind.Short); - ShortBuffer elements = bytes.asShortBuffer(); + MemoryBuffer memoryBuffer = allocateDirect(data.length, JavaKind.Short); + ShortBuffer elements = memoryBuffer.buffer().asShortBuffer(); elements.put(data); - return NativeUtils.byteBufferPointer(bytes); + return new RawPointer(memoryBuffer.address()); } @JniImpl @TruffleBoundary public @Pointer TruffleObject GetIntArrayElements(@JavaType(int[].class) StaticObject array, @Pointer TruffleObject isCopyPtr, @Inject EspressoLanguage language) { if (!getUncached().isNull(isCopyPtr)) { - ByteBuffer isCopyBuf = NativeUtils.directByteBuffer(isCopyPtr, 1); + ByteBuffer isCopyBuf = NativeUtils.wrapNativeMemoryOrThrow(isCopyPtr, 1, nativeMemory, getMeta()); isCopyBuf.put((byte) 1); // Always copy since pinning is not supported. } int[] data = array.unwrap(language); - ByteBuffer bytes = allocateDirect(data.length, JavaKind.Int); - IntBuffer elements = bytes.asIntBuffer(); + MemoryBuffer memoryBuffer = allocateDirect(data.length, JavaKind.Int); + IntBuffer elements = memoryBuffer.buffer().asIntBuffer(); elements.put(data); - return NativeUtils.byteBufferPointer(bytes); + return new RawPointer(memoryBuffer.address()); } @JniImpl @TruffleBoundary public @Pointer TruffleObject GetFloatArrayElements(@JavaType(float[].class) StaticObject array, @Pointer TruffleObject isCopyPtr, @Inject EspressoLanguage language) { if (!getUncached().isNull(isCopyPtr)) { - ByteBuffer isCopyBuf = NativeUtils.directByteBuffer(isCopyPtr, 1); + ByteBuffer isCopyBuf = NativeUtils.wrapNativeMemoryOrThrow(isCopyPtr, 1, nativeMemory, getMeta()); isCopyBuf.put((byte) 1); // Always copy since pinning is not supported. } float[] data = array.unwrap(language); - ByteBuffer bytes = allocateDirect(data.length, JavaKind.Float); - FloatBuffer elements = bytes.asFloatBuffer(); + MemoryBuffer memoryBuffer = allocateDirect(data.length, JavaKind.Float); + FloatBuffer elements = memoryBuffer.buffer().asFloatBuffer(); elements.put(data); - return NativeUtils.byteBufferPointer(bytes); + return new RawPointer(memoryBuffer.address()); } @JniImpl @TruffleBoundary public @Pointer TruffleObject GetDoubleArrayElements(@JavaType(double[].class) StaticObject array, @Pointer TruffleObject isCopyPtr, @Inject EspressoLanguage language) { if (!getUncached().isNull(isCopyPtr)) { - ByteBuffer isCopyBuf = NativeUtils.directByteBuffer(isCopyPtr, 1); + ByteBuffer isCopyBuf = NativeUtils.wrapNativeMemoryOrThrow(isCopyPtr, 1, nativeMemory, getMeta()); isCopyBuf.put((byte) 1); // Always copy since pinning is not supported. } double[] data = array.unwrap(language); - ByteBuffer bytes = allocateDirect(data.length, JavaKind.Double); - DoubleBuffer elements = bytes.asDoubleBuffer(); + MemoryBuffer memoryBuffer = allocateDirect(data.length, JavaKind.Double); + DoubleBuffer elements = memoryBuffer.buffer().asDoubleBuffer(); elements.put(data); - return NativeUtils.byteBufferPointer(bytes); + return new RawPointer(memoryBuffer.address()); } @JniImpl @TruffleBoundary public @Pointer TruffleObject GetLongArrayElements(@JavaType(long[].class) StaticObject array, @Pointer TruffleObject isCopyPtr, @Inject EspressoLanguage language) { if (!getUncached().isNull(isCopyPtr)) { - ByteBuffer isCopyBuf = NativeUtils.directByteBuffer(isCopyPtr, 1); + ByteBuffer isCopyBuf = NativeUtils.wrapNativeMemoryOrThrow(isCopyPtr, 1, nativeMemory, getMeta()); isCopyBuf.put((byte) 1); // Always copy since pinning is not supported. } long[] data = array.unwrap(language); - ByteBuffer bytes = allocateDirect(data.length, JavaKind.Long); - LongBuffer elements = bytes.asLongBuffer(); + MemoryBuffer memoryBuffer = allocateDirect(data.length, JavaKind.Long); + LongBuffer elements = memoryBuffer.buffer().asLongBuffer(); elements.put(data); - return NativeUtils.byteBufferPointer(bytes); + return new RawPointer(memoryBuffer.address()); } // endregion Get*ArrayElements @@ -2185,14 +2220,21 @@ public long GetDirectBufferCapacity(@JavaType(Buffer.class) StaticObject buf) { @JniImpl @TruffleBoundary public int RegisterNative(@JavaType(Class.class) StaticObject clazz, @Pointer TruffleObject methodNamePtr, @Pointer TruffleObject methodSignaturePtr, @Pointer TruffleObject closure) { - String methodName = NativeUtils.interopPointerToString(methodNamePtr); - String methodSignature = NativeUtils.interopPointerToString(methodSignaturePtr); + Meta meta = getMeta(); + String methodName; + String methodSignature; + try { + methodName = NativeUtils.interopPointerToString(methodNamePtr, nativeMemory); + methodSignature = NativeUtils.interopPointerToString(methodSignaturePtr, nativeMemory); + } catch (IllegalMemoryAccessException e) { + getLanguage().setPendingException(meta.createInternalError(e.toString())); + return JNI_ERR; + } assert methodName != null && methodSignature != null; Symbol name = getNames().lookup(methodName); Symbol signature = getSignatures().lookupValidSignature(methodSignature); - Meta meta = getMeta(); if (name == null || signature == null) { return handleNoSuchMethod(meta); } @@ -2683,7 +2725,7 @@ public static int GetArrayLength(@JavaType(Object.class) StaticObject array) { @JniImpl public @Pointer TruffleObject GetPrimitiveArrayCritical(@JavaType(Object.class) StaticObject object, @Pointer TruffleObject isCopyPtr, @Inject EspressoLanguage language) { if (!getUncached().isNull(isCopyPtr)) { - ByteBuffer isCopyBuf = NativeUtils.directByteBuffer(isCopyPtr, 1); + ByteBuffer isCopyBuf = NativeUtils.wrapNativeMemoryOrThrow(isCopyPtr, 1, nativeMemory, getMeta()); isCopyBuf.put((byte) 1); // Always copy since pinning is not supported. } StaticObject array = object; @@ -2692,9 +2734,8 @@ public static int GetArrayLength(@JavaType(Object.class) StaticObject array) { assert componentKind.isPrimitive(); int length = GetArrayLength(array); - ByteBuffer region = allocateDirect(length, componentKind); @Pointer - TruffleObject addressPtr = NativeUtils.byteBufferPointer(region); + TruffleObject addressPtr = new RawPointer(allocateDirect(length, componentKind).address()); // @formatter:off switch (componentKind) { case Boolean : GetBooleanArrayRegion(array, 0, length, addressPtr, language); break; @@ -2836,7 +2877,7 @@ public void ReleasePrimitiveArrayCritical(@JavaType(Object.class) StaticObject o @TruffleBoundary @JniImpl public @JavaType(Class.class) StaticObject FindClass(@Pointer TruffleObject namePtr, @Inject SubstitutionProfiler profiler) { - String name = NativeUtils.interopPointerToString(namePtr); + String name = NativeUtils.interopPointerToStringOrThrow(namePtr, nativeMemory, getMeta()); Meta meta = getMeta(); if (name == null || (name.indexOf('.') > -1)) { profiler.profile(7); @@ -3003,6 +3044,5 @@ public boolean IsVirtualThread(@JavaType(Thread.class) StaticObject thread) { } return meta.java_lang_BaseVirtualThread.isAssignableFrom(thread.getKlass()); } - // Checkstyle: resume method name check } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jni/NativeEnv.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jni/NativeEnv.java index cee9abd43589..d3452737b92b 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jni/NativeEnv.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jni/NativeEnv.java @@ -176,7 +176,7 @@ private TruffleObject getLookupCallbackClosure() { @TruffleBoundary public Object call(Object... args) { try { - String name = NativeUtils.interopPointerToString((TruffleObject) args[0]); + String name = NativeUtils.interopPointerToString((TruffleObject) args[0], getNativeAccess().nativeMemory()); CallableFromNative.Factory factory = lookupFactory(name); processCallBackResult(name, factory, args); return createNativeClosureForFactory(factory, name); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jni/RawBuffer.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jni/RawBuffer.java index 4fa20d7255b4..8878959765f1 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jni/RawBuffer.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jni/RawBuffer.java @@ -33,7 +33,10 @@ import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.espresso.classfile.JavaKind; import com.oracle.truffle.espresso.classfile.descriptors.ByteSequence; -import com.oracle.truffle.espresso.ffi.nfi.NativeUtils; +import com.oracle.truffle.espresso.ffi.RawPointer; +import com.oracle.truffle.espresso.ffi.memory.MemoryBuffer; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory.MemoryAllocationException; import com.oracle.truffle.espresso.impl.ArrayKlass; import com.oracle.truffle.espresso.meta.EspressoError; import com.oracle.truffle.espresso.runtime.EspressoContext; @@ -41,21 +44,22 @@ public final class RawBuffer implements AutoCloseable { private ByteBuffer buffer; - private TruffleObject pointer; + private final MemoryBuffer memoryBuffer; - public RawBuffer(ByteBuffer buffer, TruffleObject pointer) { + public RawBuffer(ByteBuffer buffer, MemoryBuffer memoryBuffer) { this.buffer = buffer; - this.pointer = pointer; + this.memoryBuffer = memoryBuffer; } - public static RawBuffer getNativeString(ByteSequence seq) { - ByteBuffer bb = NativeUtils.allocateDirect(seq.length() + 1); + public static RawBuffer getNativeString(ByteSequence seq, NativeMemory nativeMemory) throws MemoryAllocationException { + MemoryBuffer allocateMemoryBuffer = nativeMemory.allocateMemoryBuffer(seq.length() + 1); + ByteBuffer bb = allocateMemoryBuffer.buffer(); seq.writeTo(bb); bb.put((byte) 0); - return new RawBuffer(bb, NativeUtils.byteBufferPointer(bb)); + return new RawBuffer(bb, allocateMemoryBuffer); } - public static RawBuffer getNativeString(String name) { + public static RawBuffer getNativeString(String name, NativeMemory nativeMemory) throws MemoryAllocationException { CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder(); int length = ((int) (name.length() * encoder.averageBytesPerChar())) + 1; for (;;) { @@ -63,7 +67,9 @@ public static RawBuffer getNativeString(String name) { throw EspressoError.shouldNotReachHere(); } // Be super safe with the size of the buffer. - ByteBuffer bb = NativeUtils.allocateDirect(length); + MemoryBuffer memoryBuffer = nativeMemory.allocateMemoryBuffer(length); + + ByteBuffer bb = memoryBuffer.buffer(); encoder.reset(); CoderResult result = encoder.encode(CharBuffer.wrap(name), bb, true); @@ -75,7 +81,7 @@ public static RawBuffer getNativeString(String name) { if (result.isUnderflow() && (bb.position() < bb.capacity())) { // Encoder encoded entire string, and we have one byte of leeway. bb.put((byte) 0); - return new RawBuffer(bb, NativeUtils.byteBufferPointer(bb)); + return new RawBuffer(bb, memoryBuffer); } if (result.isOverflow() || result.isUnderflow()) { length += 1; @@ -89,14 +95,16 @@ public static RawBuffer getNativeString(String name) { } public TruffleObject pointer() { - return pointer; + return new RawPointer(memoryBuffer.address()); } @TruffleBoundary - public static RawBuffer getNativeHeapPointer(StaticObject obj, EspressoContext ctx) { + public static RawBuffer getNativeHeapPointer(StaticObject obj, EspressoContext ctx) throws MemoryAllocationException { assert obj.getKlass().isArray(); JavaKind componentKind = ((ArrayKlass) obj.getKlass()).getComponentType().getJavaKind(); - ByteBuffer bb = NativeUtils.allocateDirect(obj.length(ctx.getLanguage()) * componentKind.getByteCount()); + int bbSize = obj.length(ctx.getLanguage()) * componentKind.getByteCount(); + MemoryBuffer allocateMemoryBuffer = ctx.getNativeAccess().nativeMemory().allocateMemoryBuffer(bbSize); + ByteBuffer bb = allocateMemoryBuffer.buffer(); switch (componentKind) { case Boolean -> { boolean[] array = obj.unwrap(ctx.getLanguage()); @@ -148,7 +156,7 @@ public static RawBuffer getNativeHeapPointer(StaticObject obj, EspressoContext c } default -> throw ctx.getMeta().throwExceptionWithMessage(ctx.getMeta().java_lang_IllegalArgumentException, "Unsupported java heap access in downcall stub: " + obj.getKlass()); } - return new RawBuffer(bb, NativeUtils.byteBufferPointer(bb)); + return new RawBuffer(bb, allocateMemoryBuffer); } @TruffleBoundary @@ -212,6 +220,7 @@ public void writeBack(StaticObject obj, EspressoContext ctx) { @Override public void close() { buffer.clear(); + memoryBuffer.close(); this.buffer = null; } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jvmti/JVMTIEnv.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jvmti/JVMTIEnv.java index 4700fb606f17..7df823a01712 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jvmti/JVMTIEnv.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jvmti/JVMTIEnv.java @@ -22,6 +22,8 @@ */ package com.oracle.truffle.espresso.jvmti; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.IllegalMemoryAccessException; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.MemoryAllocationException; import static com.oracle.truffle.espresso.jvmti.JvmtiErrorCodes.JVMTI_ERROR_ILLEGAL_ARGUMENT; import static com.oracle.truffle.espresso.jvmti.JvmtiErrorCodes.JVMTI_OK; @@ -106,23 +108,33 @@ public int Allocate(long byteCount, @Pointer TruffleObject memPtr) { return JVMTI_ERROR_ILLEGAL_ARGUMENT; } TruffleObject alloc; - if (byteCount == 0) { - alloc = RawPointer.nullInstance(); - } else { - alloc = getNativeAccess().allocateMemory(byteCount); - if (getUncached().isNull(alloc)) { - return JvmtiErrorCodes.JVMTI_ERROR_OUT_OF_MEMORY; + try { + if (byteCount == 0) { + alloc = RawPointer.nullInstance(); + } else { + alloc = RawPointer.create(getNativeAccess().nativeMemory().allocateMemory(byteCount)); } + NativeUtils.writeToPointerPointer(getUncached(), memPtr, alloc, getNativeAccess().nativeMemory()); + } catch (MemoryAllocationException e) { + return JvmtiErrorCodes.JVMTI_ERROR_OUT_OF_MEMORY; + } catch (IllegalMemoryAccessException e) { + getLogger().warning("memPtr does not point to a valid memory region"); + return JVMTI_ERROR_ILLEGAL_ARGUMENT; } - NativeUtils.writeToPointerPointer(getUncached(), memPtr, alloc); return JVMTI_OK; } @JvmtiImpl + @SuppressWarnings("unchecked") public int Deallocate(@Pointer TruffleObject memPtr) { // Null is valid. Do nothing if that is the case if (!getUncached().isNull(memPtr)) { - getNativeAccess().freeMemory(memPtr); + try { + getNativeAccess().nativeMemory().freeMemory(NativeUtils.interopAsPointer(memPtr)); + } catch (IllegalMemoryAccessException e) { + getLogger().warning("memPtr does not point to a valid memory region"); + return JVMTI_ERROR_ILLEGAL_ARGUMENT; + } } return JVMTI_OK; } @@ -145,7 +157,12 @@ public int GetEnvironmentLocalStorage(@Pointer TruffleObject dataPtr) { // Pointer should have been pre-null-checked return JVMTI_ERROR_ILLEGAL_ARGUMENT; } - NativeUtils.writeToPointerPointer(getUncached(), dataPtr, envLocalStorage); + try { + NativeUtils.writeToPointerPointer(getUncached(), dataPtr, envLocalStorage, getNativeAccess().nativeMemory()); + } catch (IllegalMemoryAccessException e) { + getLogger().warning("dataPtr does not point to a valid memory region"); + return JVMTI_ERROR_ILLEGAL_ARGUMENT; + } return JVMTI_OK; } @@ -155,7 +172,12 @@ public int GetPhase(@Pointer TruffleObject phasePtr) { // Pointer should have been pre-null-checked return JVMTI_ERROR_ILLEGAL_ARGUMENT; } - NativeUtils.writeToIntPointer(getUncached(), phasePtr, getVM().getJvmti().getPhase()); + try { + NativeUtils.writeToIntPointer(getUncached(), phasePtr, getVM().getJvmti().getPhase(), getNativeAccess().nativeMemory()); + } catch (IllegalMemoryAccessException e) { + getLogger().warning("phasePtr does not point to a valid memory region"); + return JVMTI_ERROR_ILLEGAL_ARGUMENT; + } return JVMTI_OK; } @@ -172,7 +194,12 @@ public int GetVersionNumber(@Pointer TruffleObject versionPtr) { // Pointer should have been pre-null-checked return JVMTI_ERROR_ILLEGAL_ARGUMENT; } - NativeUtils.writeToIntPointer(getUncached(), versionPtr, jvmtiVersion); + try { + NativeUtils.writeToIntPointer(getUncached(), versionPtr, jvmtiVersion, getNativeAccess().nativeMemory()); + } catch (IllegalMemoryAccessException e) { + getLogger().warning("versionPtr does not point to a valid memory region"); + return JVMTI_ERROR_ILLEGAL_ARGUMENT; + } return JVMTI_OK; } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/Meta.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/Meta.java index 70d652f3f8a3..a909a928d59d 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/Meta.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/Meta.java @@ -2763,6 +2763,16 @@ public EspressoException throwIllegalArgumentExceptionBoundary() { throw throwException(java_lang_IllegalArgumentException); } + @TruffleBoundary + public EspressoException throwInternalErrorBoundary(String message) { + throw throwExceptionWithMessage(java_lang_InternalError, message); + } + + @TruffleBoundary + public EspressoException createInternalError(String message) { + return EspressoException.wrap(initExceptionWithMessage(java_lang_InternalError, message), this); + } + @TruffleBoundary public EspressoException throwIllegalArgumentExceptionBoundary(String message) { throw throwExceptionWithMessage(java_lang_IllegalArgumentException, message); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/AgentLibraries.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/AgentLibraries.java index 2eef4d6bfbc9..020910be4609 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/AgentLibraries.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/AgentLibraries.java @@ -41,10 +41,12 @@ import com.oracle.truffle.espresso.ffi.NativeSignature; import com.oracle.truffle.espresso.ffi.NativeType; import com.oracle.truffle.espresso.ffi.RawPointer; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory.MemoryAllocationException; import com.oracle.truffle.espresso.impl.ContextAccessImpl; import com.oracle.truffle.espresso.jni.RawBuffer; import com.oracle.truffle.espresso.jvmti.JvmtiPhase; import com.oracle.truffle.espresso.meta.EspressoError; +import com.oracle.truffle.espresso.meta.Meta; final class AgentLibraries extends ContextAccessImpl { @@ -82,7 +84,7 @@ void initialize() { if (onLoad == null) { throw getContext().abort("Unable to locate " + AGENT_ONLOAD + " in agent " + agent.name); } - try (RawBuffer optionBuffer = RawBuffer.getNativeString(agent.options)) { + try (RawBuffer optionBuffer = RawBuffer.getNativeString(agent.options, getContext().getNativeAccess().nativeMemory())) { ret = interop.execute(onLoad, getContext().getVM().getJavaVM(), optionBuffer.pointer(), RawPointer.nullInstance()); assert interop.fitsInInt(ret); if (interop.asInt(ret) != JNI_OK) { @@ -91,6 +93,9 @@ void initialize() { } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) { CompilerDirectives.transferToInterpreterAndInvalidate(); throw EspressoError.shouldNotReachHere(e); + } catch (MemoryAllocationException e) { + Meta meta = getContext().getMeta(); + throw meta.throwExceptionWithMessage(meta.java_lang_OutOfMemoryError, e.getMessage(), getContext()); } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/JImageLibrary.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/JImageLibrary.java index c4efb29660c9..d82d4797d4e7 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/JImageLibrary.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/JImageLibrary.java @@ -24,8 +24,6 @@ import static com.oracle.truffle.espresso.runtime.Classpath.JAVA_BASE; -import java.nio.ByteBuffer; - import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.interop.ArityException; import com.oracle.truffle.api.interop.InteropLibrary; @@ -39,11 +37,15 @@ import com.oracle.truffle.espresso.descriptors.EspressoSymbols.Names; import com.oracle.truffle.espresso.ffi.NativeSignature; import com.oracle.truffle.espresso.ffi.NativeType; -import com.oracle.truffle.espresso.ffi.nfi.NativeUtils; +import com.oracle.truffle.espresso.ffi.RawPointer; +import com.oracle.truffle.espresso.ffi.memory.MemoryBuffer; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory.MemoryAllocationException; import com.oracle.truffle.espresso.impl.ContextAccessImpl; import com.oracle.truffle.espresso.impl.PackageTable.PackageEntry; import com.oracle.truffle.espresso.jni.RawBuffer; import com.oracle.truffle.espresso.meta.EspressoError; +import com.oracle.truffle.espresso.meta.Meta; @SuppressWarnings("unused") final class JImageLibrary extends ContextAccessImpl { @@ -108,46 +110,64 @@ final class JImageLibrary extends ContextAccessImpl { findResource = getNativeAccess().lookupAndBindSymbol(jimageLibrary, FIND_RESOURCE, FIND_RESOURCE_SIGNATURE); getResource = getNativeAccess().lookupAndBindSymbol(jimageLibrary, GET_RESOURCE, GET_RESOURCE_SIGNATURE); - this.javaBaseBuffer = RawBuffer.getNativeString(JAVA_BASE); - this.versionBuffer = RawBuffer.getNativeString(VERSION_STRING); - this.emptyStringBuffer = RawBuffer.getNativeString(""); - + NativeMemory nativeMemory = getContext().getNativeAccess().nativeMemory(); + try { + this.javaBaseBuffer = RawBuffer.getNativeString(JAVA_BASE, nativeMemory); + this.versionBuffer = RawBuffer.getNativeString(VERSION_STRING, nativeMemory); + this.emptyStringBuffer = RawBuffer.getNativeString("", nativeMemory); + } catch (MemoryAllocationException e) { + throw throwOutOfMemory(e); + } this.uncached = InteropLibrary.getFactory().getUncached(); } public TruffleObject open(String name) { - ByteBuffer error = NativeUtils.allocateDirect(JavaKind.Int.getByteCount()); - try (RawBuffer nameBuffer = RawBuffer.getNativeString(name)) { - return (TruffleObject) execute(open, nameBuffer.pointer(), NativeUtils.byteBufferPointer(error)); + NativeMemory nativeMemory = getContext().getNativeAccess().nativeMemory(); + try (MemoryBuffer errorBuffer = nativeMemory.allocateMemoryBuffer(JavaKind.Int.getByteCount()); // + RawBuffer nameBuffer = RawBuffer.getNativeString(name, nativeMemory)) { + return (TruffleObject) execute(open, nameBuffer.pointer(), new RawPointer(errorBuffer.address())); + } catch (MemoryAllocationException e) { + throw throwOutOfMemory(e); } } + private EspressoException throwOutOfMemory(MemoryAllocationException e) { + Meta meta = getContext().getMeta(); + return meta.throwExceptionWithMessage(meta.java_lang_OutOfMemoryError, e.getMessage(), getContext()); + } + public void close(TruffleObject jimage) { execute(close, jimage); + this.javaBaseBuffer.close(); + this.versionBuffer.close(); + this.emptyStringBuffer.close(); } public byte[] getClassBytes(TruffleObject jimage, ByteSequence name) { + NativeMemory nativeMemory = getContext().getNativeAccess().nativeMemory(); // Prepare calls - ByteBuffer sizeBuffer = NativeUtils.allocateDirect(JavaKind.Long.getByteCount()); - TruffleObject sizePtr = NativeUtils.byteBufferPointer(sizeBuffer); - - long location = findLocation(jimage, sizePtr, name); - if (location == 0) { - return null; + try (MemoryBuffer sizeBuffer = nativeMemory.allocateMemoryBuffer(JavaKind.Long.getByteCount())) { + TruffleObject sizePtr = new RawPointer(sizeBuffer.address()); + long location = findLocation(jimage, sizePtr, name); + if (location == 0) { + return null; + } + // Extract the result + long capacity = sizeBuffer.buffer().getLong(0); + try (MemoryBuffer allocateMemoryBuffer = nativeMemory.allocateMemoryBuffer((int) capacity)) { + execute(getResource, jimage, location, new RawPointer(allocateMemoryBuffer.address()), capacity); + byte[] result = new byte[(int) capacity]; + allocateMemoryBuffer.buffer().get(result); + return result; + } + } catch (MemoryAllocationException e) { + throw throwOutOfMemory(e); } - - // Extract the result - long capacity = sizeBuffer.getLong(0); - ByteBuffer bytes = NativeUtils.allocateDirect((int) capacity); - TruffleObject bytesPtr = NativeUtils.byteBufferPointer(bytes); - execute(getResource, jimage, location, bytesPtr, capacity); - byte[] result = new byte[(int) capacity]; - bytes.get(result); - return result; } private long findLocation(TruffleObject jimage, TruffleObject sizePtr, ByteSequence name) { - try (RawBuffer nameBuffer = RawBuffer.getNativeString(name)) { + NativeMemory nativeMemory = getContext().getNativeAccess().nativeMemory(); + try (RawBuffer nameBuffer = RawBuffer.getNativeString(name, nativeMemory)) { TruffleObject namePtr = nameBuffer.pointer(); long location = (long) execute(findResource, jimage, emptyStringBuffer.pointer(), versionBuffer.pointer(), namePtr, sizePtr); if (location != 0) { @@ -168,7 +188,7 @@ private long findLocation(TruffleObject jimage, TruffleObject sizePtr, ByteSeque return location; } TruffleObject moduleName; - try (RawBuffer pkgBuffer = RawBuffer.getNativeString(pkg)) { + try (RawBuffer pkgBuffer = RawBuffer.getNativeString(pkg, nativeMemory)) { moduleName = (TruffleObject) execute(packageToModule, jimage, pkgBuffer.pointer()); } if (uncached.isNull(moduleName)) { @@ -189,12 +209,15 @@ private long findLocation(TruffleObject jimage, TruffleObject sizePtr, ByteSeque return (long) execute(findResource, jimage, javaBaseBuffer.pointer(), versionBuffer.pointer(), namePtr, sizePtr); } else { String nameAsString = moduleName == null ? "" : moduleName.toString(); - try (RawBuffer moduleNameBuffer = RawBuffer.getNativeString(nameAsString)) { + try (RawBuffer moduleNameBuffer = RawBuffer.getNativeString(nameAsString, nativeMemory)) { return (long) execute(findResource, jimage, moduleNameBuffer.pointer(), versionBuffer.pointer(), namePtr, sizePtr); } } } + } catch (MemoryAllocationException e) { + throw throwOutOfMemory(e); } + } private static ByteSequence packageFromName(ByteSequence name) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/panama/DowncallStubs.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/panama/DowncallStubs.java index bafbd574c885..3c0926b13727 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/panama/DowncallStubs.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/panama/DowncallStubs.java @@ -22,6 +22,8 @@ */ package com.oracle.truffle.espresso.runtime.panama; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.MemoryAllocationException; + import java.util.Arrays; import java.util.EnumSet; import java.util.concurrent.atomic.AtomicInteger; @@ -312,9 +314,15 @@ private static Object handlePointerArg(RawBuffer.Buffers buffers, EspressoContex CompilerDirectives.transferToInterpreterAndInvalidate(); throw ctx.getMeta().throwExceptionWithMessage(ctx.getMeta().java_lang_InternalError, "Unsupported java heap access in downcall stub: " + obj.getKlass()); } - RawBuffer buffer = RawBuffer.getNativeHeapPointer(obj, ctx); - buffers.add(buffer, obj); - return buffer.pointer(); + try { + RawBuffer buffer = RawBuffer.getNativeHeapPointer(obj, ctx); + buffers.add(buffer, obj); + return buffer.pointer(); + } catch (MemoryAllocationException e) { + Meta meta = ctx.getMeta(); + CompilerDirectives.transferToInterpreter(); + throw meta.throwExceptionWithMessage(meta.java_lang_OutOfMemoryError, e.getMessage(), ctx); + } } @TruffleBoundary diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_jdk_vm_ci_services_Services.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_jdk_vm_ci_services_Services.java index 4308671ec130..9683ece73e07 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_jdk_vm_ci_services_Services.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_jdk_vm_ci_services_Services.java @@ -22,16 +22,21 @@ */ package com.oracle.truffle.espresso.substitutions.jvmci; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.IllegalMemoryAccessException; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.MemoryAllocationException; import static com.oracle.truffle.espresso.substitutions.jvmci.Target_jdk_vm_ci_runtime_JVMCI.checkJVMCIAvailable; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Map; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.espresso.EspressoLanguage; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; import com.oracle.truffle.espresso.ffi.nfi.NativeUtils; +import com.oracle.truffle.espresso.meta.EspressoError; +import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; @@ -53,7 +58,7 @@ private Target_jdk_vm_ci_services_Services() { @Substitution @TruffleBoundary public static long readSystemPropertiesInfo(@JavaType(int[].class) StaticObject offsets, - @Inject EspressoLanguage language, @Inject EspressoContext context) { + @Inject EspressoLanguage language, @Inject EspressoContext context, @Inject Meta meta) { checkJVMCIAvailable(context.getLanguage()); int[] unwrappedOffsets = offsets.unwrap(language); unwrappedOffsets[0] = NODE_STRUCT_NEXT_OFFSET; @@ -91,9 +96,21 @@ public static long readSystemPropertiesInfo(@JavaType(int[].class) StaticObject } // Allocate the buffer - TruffleObject allocated = context.getNativeAccess().allocateMemory(size); - long ptr = NativeUtils.interopAsPointer(allocated); - ByteBuffer buffer = NativeUtils.directByteBuffer(ptr, size); + NativeMemory nativeMemory = context.getNativeAccess().nativeMemory(); + long ptr = 0; + try { + ptr = nativeMemory.allocateMemory(size); + } catch (MemoryAllocationException e) { + throw meta.throwExceptionWithMessage(meta.java_lang_OutOfMemoryError, e.getMessage(), context); + } + ByteBuffer buffer; + try { + buffer = NativeUtils.wrapNativeMemory(ptr, size, nativeMemory); + } catch (IllegalMemoryAccessException e) { + // we should not reach here since we have just a moment ago successfully allocated ptr. + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere(e); + } for (i = 0; i < keys.length; i++) { /* Layout: * @formatter:off diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_sun_misc_Unsafe.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_sun_misc_Unsafe.java index d65e0de44e1b..f85432910274 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_sun_misc_Unsafe.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_sun_misc_Unsafe.java @@ -22,6 +22,8 @@ */ package com.oracle.truffle.espresso.substitutions.standard; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.IllegalMemoryAccessException; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.MemoryAllocationException; import static com.oracle.truffle.espresso.substitutions.SubstitutionFlag.IsTrivial; import static com.oracle.truffle.espresso.threads.ThreadState.PARKED; import static com.oracle.truffle.espresso.threads.ThreadState.TIMED_PARKED; @@ -40,9 +42,6 @@ import com.oracle.truffle.api.dsl.GenerateCached; import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.TruffleObject; -import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.InlinedBranchProfile; import com.oracle.truffle.espresso.EspressoLanguage; @@ -52,8 +51,8 @@ import com.oracle.truffle.espresso.classfile.JavaVersion; import com.oracle.truffle.espresso.classfile.descriptors.Name; import com.oracle.truffle.espresso.classfile.descriptors.Symbol; -import com.oracle.truffle.espresso.ffi.Buffer; -import com.oracle.truffle.espresso.ffi.RawPointer; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory.MemoryAccessMode; import com.oracle.truffle.espresso.impl.ArrayKlass; import com.oracle.truffle.espresso.impl.ClassRegistry; import com.oracle.truffle.espresso.impl.EspressoClassLoadingException; @@ -87,7 +86,9 @@ @EspressoSubstitutions(nameProvider = Target_sun_misc_Unsafe.SharedUnsafe.class) public final class Target_sun_misc_Unsafe { - /** The value of {@code addressSize()}. */ + /** + * The value of {@code addressSize()}. + */ public static final int ADDRESS_SIZE; private static final int SAFETY_FIELD_OFFSET = 123456789; @@ -441,9 +442,7 @@ public static void registerNatives() { * * @throws IllegalArgumentException if the size is negative or too large for the native size_t * type - * * @throws OutOfMemoryError if the allocation is refused by the system - * * @see GetByte * @see PutByte */ @@ -453,20 +452,11 @@ public static long allocateMemory(@SuppressWarnings("unused") @JavaType(Unsafe.c if (length < 0) { throw meta.throwExceptionWithMessage(meta.java_lang_IllegalArgumentException, "requested size is negative"); } - @Buffer - TruffleObject buffer = meta.getNativeAccess().allocateMemory(length); - if (buffer == null && length > 0) { - // malloc may return anything for 0-sized allocations. - throw meta.throwExceptionWithMessage(meta.java_lang_OutOfMemoryError, "malloc returned NULL"); - } - long ptr; try { - ptr = InteropLibrary.getUncached().asPointer(buffer); - } catch (UnsupportedMessageException e) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw EspressoError.shouldNotReachHere(e); + return meta.getNativeAccess().nativeMemory().allocateMemory(length); + } catch (MemoryAllocationException e) { + throw meta.throwExceptionWithMessage(meta.java_lang_OutOfMemoryError, e.getMessage()); } - return ptr; } /** @@ -479,9 +469,7 @@ public static long allocateMemory(@SuppressWarnings("unused") @JavaType(Unsafe.c * * @throws IllegalArgumentException if the size is negative or too large for the native size_t * type - * * @throws OutOfMemoryError if the allocation is refused by the system - * * @see #allocateMemory */ @TruffleBoundary @@ -490,19 +478,13 @@ public static long reallocateMemory(@SuppressWarnings("unused") @JavaType(Unsafe if (newSize < 0) { throw meta.throwExceptionWithMessage(meta.java_lang_IllegalArgumentException, "requested size is negative"); } - @Buffer - TruffleObject result = meta.getNativeAccess().reallocateMemory(RawPointer.create(address), newSize); - if (result == null) { - throw meta.throwExceptionWithMessage(meta.java_lang_OutOfMemoryError, "realloc couldn't reallocate " + newSize + " bytes"); - } - long newAddress; try { - newAddress = InteropLibrary.getUncached().asPointer(result); - } catch (UnsupportedMessageException e) { - CompilerDirectives.transferToInterpreter(); - throw EspressoError.shouldNotReachHere(e); + return meta.getNativeAccess().nativeMemory().reallocateMemory(address, newSize); + } catch (MemoryAllocationException e) { + throw meta.throwExceptionWithMessage(meta.java_lang_OutOfMemoryError, e.getMessage()); + } catch (IllegalMemoryAccessException e) { + throw meta.throwIllegalArgumentExceptionBoundary("address should refer to a value returned by allocate!"); } - return newAddress; } /** @@ -515,7 +497,11 @@ public static long reallocateMemory(@SuppressWarnings("unused") @JavaType(Unsafe @TruffleBoundary @Substitution(hasReceiver = true, nameProvider = SharedUnsafeAppend0.class) public static void freeMemory(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, @Inject Meta meta) { - meta.getNativeAccess().freeMemory(RawPointer.create(address)); + try { + meta.getNativeAccess().nativeMemory().freeMemory(address); + } catch (IllegalMemoryAccessException e) { + throw meta.throwIllegalArgumentExceptionBoundary("address should refer to a value returned by allocate!"); + } } @Substitution(hasReceiver = true, nameProvider = SharedUnsafeAppend0.class) @@ -541,21 +527,56 @@ public static void ensureClassInitialized(@SuppressWarnings("unused") @JavaType( @Substitution(hasReceiver = true, nameProvider = SharedUnsafeAppend0.class) public static void copyMemory(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject srcBase, long srcOffset, - @JavaType(Object.class) StaticObject destBase, long destOffset, long bytes, @Inject EspressoLanguage language, @Inject Meta meta) { + @JavaType(Object.class) StaticObject dstBase, long dstOffset, long bytes, @Inject EspressoLanguage language, @Inject Meta meta, @Inject EspressoContext context) { if (bytes == 0) { return; } - UnsafeAccess.getIfAllowed(meta).copyMemory(MetaUtil.unwrapArrayOrNull(language, srcBase), srcOffset, MetaUtil.unwrapArrayOrNull(language, destBase), destOffset, bytes); + // If we are doing off-heap accesses use nativeMemory + Object src = MetaUtil.unwrapArrayOrNull(language, srcBase); + Object dst = MetaUtil.unwrapArrayOrNull(language, dstBase); + NativeMemory nativeMemory = context.getNativeAccess().nativeMemory(); + try { + if (src == null && dst == null) { + nativeMemory.copyMemory(srcOffset, dstOffset, bytes); + return; + } + // todo(GR-71081) + Unsafe unsafe = UnsafeAccess.getIfAllowed(meta); + int byteArrayBaseOffset = unsafe.arrayBaseOffset(byte[].class); + if (src == null) { + byte[] srcArray = new byte[Math.toIntExact(bytes)]; + nativeMemory.readMemory(srcOffset, srcArray); + unsafe.copyMemory(srcArray, byteArrayBaseOffset, dst, dstOffset, bytes); + return; + } + if (dst == null) { + byte[] buff = new byte[Math.toIntExact(bytes)]; + unsafe.copyMemory(src, srcOffset, buff, byteArrayBaseOffset, bytes); + nativeMemory.writeMemory(dstOffset, buff); + return; + } + unsafe.copyMemory(src, srcOffset, dst, dstOffset, bytes); + } catch (IllegalMemoryAccessException e) { + throw meta.throwIllegalArgumentExceptionBoundary("Invalid memory access: srcOffset or dstOffset and size refer to memory outside the allocated region"); + } } @Substitution(hasReceiver = true) public static void copySwapMemory0(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject srcBase, long srcOffset, - @JavaType(Object.class) StaticObject destBase, long destOffset, long bytes, long elemSize, @Inject EspressoLanguage language, @Inject Meta meta) { + @JavaType(Object.class) StaticObject dstBase, long dstOffset, long bytes, long elemSize, @Inject EspressoLanguage language, + @Inject EspressoContext context, @Inject Meta meta) { if (bytes == 0) { return; } - UnsafeAccess.checkAllowed(meta); - UnsafeSupport.copySwapMemory(MetaUtil.unwrapArrayOrNull(language, srcBase), srcOffset, MetaUtil.unwrapArrayOrNull(language, destBase), destOffset, bytes, elemSize); + // If we are doing off-heap accesses use nativeMemory + Object src = MetaUtil.unwrapArrayOrNull(language, srcBase); + Object dst = MetaUtil.unwrapArrayOrNull(language, dstBase); + try { + UnsafeSupport.copySwapMemory(src, srcOffset, dst, dstOffset, bytes, elemSize, context.getNativeAccess().nativeMemory()); + } catch (IllegalMemoryAccessException e) { + throw meta.throwIllegalArgumentExceptionBoundary("Invalid memory access: srcOffset or dstOffset and size refer to memory outside the allocated region"); + } + } /** @@ -590,17 +611,23 @@ public static void copySwapMemory0(@SuppressWarnings("unused") @JavaType(Unsafe. */ @Substitution(hasReceiver = true, nameProvider = SharedUnsafeAppend0.class) public static void setMemory(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject o, long offset, long bytes, byte value, - @Inject Meta meta, @Inject EspressoLanguage language) { + @Inject Meta meta, @Inject EspressoLanguage language, @Inject EspressoContext ctx) { + // If we are doing off-heap accesses use nativeMemory Object hostObject; if (StaticObject.isNull(o)) { - hostObject = null; + try { + ctx.getNativeAccess().nativeMemory().setMemory(offset, bytes, value); + } catch (IllegalMemoryAccessException e) { + throw meta.throwIllegalArgumentExceptionBoundary("Invalid memory access: offset and size refer to memory outside the allocated region"); + } } else if (o.getKlass().isArray()) { hostObject = o.unwrap(language); + // todo(GR-71081) + UnsafeAccess.getIfAllowed(meta).setMemory(hostObject, offset, bytes, value); } else { CompilerDirectives.transferToInterpreterAndInvalidate(); throw EspressoError.shouldNotReachHere(); } - UnsafeAccess.getIfAllowed(meta).setMemory(hostObject, offset, bytes, value); } /** @@ -768,7 +795,6 @@ private static boolean consumeUnparkSignal(StaticObject self, Meta meta) { * calling from native code. * * @param thread the thread to unpark. - * */ @TruffleBoundary(allowInlining = true) @Substitution(hasReceiver = true) @@ -865,7 +891,6 @@ public static boolean tryMonitorEnter(@SuppressWarnings("unused") @JavaType(Unsa * * @param loadavg an array of double of size nelems * @param nelems the number of samples to be retrieved and must be 1 to 3. - * * @return the number of samples actually retrieved; or -1 if the load average is unobtainable. */ @Substitution(hasReceiver = true, nameProvider = SharedUnsafeAppend0.class) @@ -945,6 +970,14 @@ protected static boolean isNullOrArray(StaticObject object) { return StaticObject.isNull(object) || object.isArray(); // order matters } + protected static boolean isNull(StaticObject object) { + return StaticObject.isNull(object); + } + + protected static boolean isArray(StaticObject object) { + return !StaticObject.isNull(object) && object.isArray(); // order matters + } + protected static Object unwrapNullOrArray(EspressoLanguage language, StaticObject object) { assert isNullOrArray(object); if (StaticObject.isNull(object)) { @@ -952,6 +985,11 @@ protected static Object unwrapNullOrArray(EspressoLanguage language, StaticObjec } return object.unwrap(language); } + + protected static Object unwrapArray(EspressoLanguage language, StaticObject object) { + assert isArray(object); + return object.unwrap(language); + } } // region put*(long offset, * value) @@ -969,8 +1007,14 @@ abstract static class PutByte extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, long address, byte value); @Specialization - void doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, byte value) { - UnsafeAccess.getIfAllowed(getMeta()).putByte(address, value); + void doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, byte value, + @Cached InlinedBranchProfile errorBranch) { + try { + getNativeAccess().nativeMemory().putByte(address, value, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } } } @@ -981,8 +1025,14 @@ abstract static class PutChar extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, long address, char value); @Specialization - void doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, char value) { - UnsafeAccess.getIfAllowed(getMeta()).putChar(address, value); + void doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, char value, + @Cached InlinedBranchProfile errorBranch) { + try { + getNativeAccess().nativeMemory().putChar(address, value, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } } } @@ -993,12 +1043,20 @@ abstract static class PutShort extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, long address, short value); @Specialization - void doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, short value) { - UnsafeAccess.getIfAllowed(getMeta()).putShort(address, value); + void doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, short value, + @Cached InlinedBranchProfile errorBranch) { + try { + getNativeAccess().nativeMemory().putShort(address, value, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } } } - /** @see GetByteWithBase */ + /** + * @see GetByteWithBase + */ @GenerateInline(false) // not available in substitutions @Substitution(hasReceiver = true) abstract static class PutInt extends UnsafeAccessNode { @@ -1006,8 +1064,14 @@ abstract static class PutInt extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, long address, int value); @Specialization - void doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, int value) { - UnsafeAccess.getIfAllowed(getMeta()).putInt(address, value); + void doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, int value, + @Cached InlinedBranchProfile errorBranch) { + try { + getNativeAccess().nativeMemory().putInt(address, value, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } } } @@ -1018,8 +1082,14 @@ abstract static class PutFloat extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, long address, float value); @Specialization - void doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, float value) { - UnsafeAccess.getIfAllowed(getMeta()).putFloat(address, value); + void doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, float value, + @Cached InlinedBranchProfile errorBranch) { + try { + getNativeAccess().nativeMemory().putFloat(address, value, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } } } @@ -1030,8 +1100,14 @@ abstract static class PutDouble extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, long address, double value); @Specialization - void doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, double value) { - UnsafeAccess.getIfAllowed(getMeta()).putDouble(address, value); + void doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, double value, + @Cached InlinedBranchProfile errorBranch) { + try { + getNativeAccess().nativeMemory().putDouble(address, value, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } } } @@ -1042,8 +1118,14 @@ abstract static class PutLong extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, long address, long value); @Specialization - void doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, long value) { - UnsafeAccess.getIfAllowed(getMeta()).putLong(address, value); + void doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, long value, + @Cached InlinedBranchProfile errorBranch) { + try { + getNativeAccess().nativeMemory().putLong(address, value, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } } } @@ -1063,9 +1145,20 @@ void doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject s public abstract static class PutByteWithBase extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, byte value); - @Specialization(guards = "isNullOrArray(holder)") - void doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, byte value) { - UnsafeAccess.getIfAllowed(getMeta()).putByte(unwrapNullOrArray(getLanguage(), holder), offset, value); + @Specialization(guards = "isNull(holder)") + void doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, byte value, + @Cached InlinedBranchProfile errorBranch) { + try { + getNativeAccess().nativeMemory().putByte(offset, value, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + void doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, byte value) { + UnsafeAccess.getIfAllowed(getMeta()).putByte(unwrapArray(getLanguage(), holder), offset, value); } @Specialization(guards = "!isNullOrArray(holder)") @@ -1103,8 +1196,16 @@ public abstract static class PutObjectWithBase extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, @JavaType(Object.class) StaticObject value); - @Specialization(guards = "isNullOrArray(holder)") - void doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, + @Specialization(guards = "isNull(holder)") + @SuppressWarnings("unused") + void doOffHeap(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offs, + @JavaType(Object.class) StaticObject value) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere("putObject* to native memory"); + } + + @Specialization(guards = "isArray(holder)") + void doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, @JavaType(Object.class) StaticObject value) { UnsafeAccess.getIfAllowed(getMeta()).putObject(unwrapNullOrArray(getLanguage(), holder), offset, value); } @@ -1143,9 +1244,20 @@ private static void doPut(Field f, StaticObject holder, StaticObject value, Meta public abstract static class PutBooleanWithBase extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, boolean value); - @Specialization(guards = "isNullOrArray(holder)") - void doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, boolean value) { - UnsafeAccess.getIfAllowed(getMeta()).putBoolean(unwrapNullOrArray(getLanguage(), holder), offset, value); + @Specialization(guards = "isNull(holder)") + void doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, boolean value, + @Cached InlinedBranchProfile errorBranch) { + try { + getNativeAccess().nativeMemory().putBoolean(offset, value, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + void doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, boolean value) { + UnsafeAccess.getIfAllowed(getMeta()).putBoolean(unwrapArray(getLanguage(), holder), offset, value); } @Specialization(guards = "!isNullOrArray(holder)") @@ -1182,8 +1294,19 @@ private static void doPut(Field f, StaticObject holder, boolean value, Meta meta public abstract static class PutCharWithBase extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, char value); - @Specialization(guards = "isNullOrArray(holder)") - void doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, char value) { + @Specialization(guards = "isNull(holder)") + void doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, char value, + @Cached InlinedBranchProfile errorBranch) { + try { + getNativeAccess().nativeMemory().putChar(offset, value, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + void doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, char value) { UnsafeAccess.getIfAllowed(getMeta()).putChar(unwrapNullOrArray(getLanguage(), holder), offset, value); } @@ -1221,8 +1344,19 @@ private static void doPut(Field f, StaticObject holder, char value, Meta meta) { public abstract static class PutShortWithBase extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, short value); - @Specialization(guards = "isNullOrArray(holder)") - void doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, short value) { + @Specialization(guards = "isNull(holder)") + void doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, short value, + @Cached InlinedBranchProfile errorBranch) { + try { + getNativeAccess().nativeMemory().putShort(offset, value, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + void doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, short value) { UnsafeAccess.getIfAllowed(getMeta()).putShort(unwrapNullOrArray(getLanguage(), holder), offset, value); } @@ -1260,8 +1394,19 @@ private static void doPut(Field f, StaticObject holder, short value, Meta meta) public abstract static class PutIntWithBase extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, int value); - @Specialization(guards = "isNullOrArray(holder)") - void doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, int value) { + @Specialization(guards = "isNull(holder)") + void doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, int value, + @Cached InlinedBranchProfile errorBranch) { + try { + getNativeAccess().nativeMemory().putInt(offset, value, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + void doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, int value) { UnsafeAccess.getIfAllowed(getMeta()).putInt(unwrapNullOrArray(getLanguage(), holder), offset, value); } @@ -1299,8 +1444,19 @@ private static void doPut(Field f, StaticObject holder, int value, Meta meta) { public abstract static class PutFloatWithBase extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, float value); - @Specialization(guards = "isNullOrArray(holder)") - void doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, float value) { + @Specialization(guards = "isNull(holder)") + void doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, float value, + @Cached InlinedBranchProfile errorBranch) { + try { + getNativeAccess().nativeMemory().putFloat(offset, value, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + void doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, float value) { UnsafeAccess.getIfAllowed(getMeta()).putFloat(unwrapNullOrArray(getLanguage(), holder), offset, value); } @@ -1338,8 +1494,19 @@ private static void doPut(Field f, StaticObject holder, float value, Meta meta) public abstract static class PutDoubleWithBase extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, double value); - @Specialization(guards = "isNullOrArray(holder)") - void doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, double value) { + @Specialization(guards = "isNull(holder)") + void doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, double value, + @Cached InlinedBranchProfile errorBranch) { + try { + getNativeAccess().nativeMemory().putDouble(offset, value, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + void doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, double value) { UnsafeAccess.getIfAllowed(getMeta()).putDouble(unwrapNullOrArray(getLanguage(), holder), offset, value); } @@ -1377,8 +1544,19 @@ private static void doPut(Field f, StaticObject holder, double value, Meta meta) public abstract static class PutLongWithBase extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, long value); - @Specialization(guards = "isNullOrArray(holder)") - void doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, long value) { + @Specialization(guards = "isNull(holder)") + void doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, long value, + @Cached InlinedBranchProfile errorBranch) { + try { + getNativeAccess().nativeMemory().putLong(offset, value, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + void doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, long value) { UnsafeAccess.getIfAllowed(getMeta()).putLong(unwrapNullOrArray(getLanguage(), holder), offset, value); } @@ -1422,8 +1600,19 @@ private static void doPut(Field f, StaticObject holder, long value, Meta meta) { public abstract static class PutOrderedInt extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, int value); - @Specialization(guards = "isNullOrArray(holder)") - void doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, int value) { + @Specialization(guards = "isNull(holder)") + void doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, int value, + @Cached InlinedBranchProfile errorBranch) { + try { + getNativeAccess().nativeMemory().putInt(offset, value, MemoryAccessMode.RELEASE_ACQUIRE); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + void doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, int value) { UnsafeAccess.getIfAllowed(getMeta()).putOrderedInt(unwrapNullOrArray(getLanguage(), holder), offset, value); } @@ -1461,8 +1650,19 @@ private static void doPut(Field f, StaticObject holder, int value, Meta meta) { public abstract static class PutOrderedLong extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, long value); - @Specialization(guards = "isNullOrArray(holder)") - void doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, long value) { + @Specialization(guards = "isNull(holder)") + void doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, long value, + @Cached InlinedBranchProfile errorBranch) { + try { + getNativeAccess().nativeMemory().putLong(offset, value, MemoryAccessMode.RELEASE_ACQUIRE); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + void doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, long value) { UnsafeAccess.getIfAllowed(getMeta()).putOrderedLong(unwrapNullOrArray(getLanguage(), holder), offset, value); } @@ -1501,8 +1701,16 @@ public abstract static class PutOrderedObject extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, @JavaType(Object.class) StaticObject value); - @Specialization(guards = "isNullOrArray(holder)") - void doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, + @Specialization(guards = "isNull(holder)") + @SuppressWarnings("unused") + void doOffHeap(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, + @JavaType(Object.class) StaticObject value) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere("put*Object to native memory"); + } + + @Specialization(guards = "isArray(holder)") + void doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, @JavaType(Object.class) StaticObject value) { UnsafeAccess.getIfAllowed(getMeta()).putOrderedObject(unwrapNullOrArray(getLanguage(), holder), offset, value); } @@ -1552,8 +1760,19 @@ private static void doPut(Field f, StaticObject holder, StaticObject value, Meta public abstract static class GetByteWithBase extends UnsafeAccessNode { abstract byte execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset); - @Specialization(guards = "isNullOrArray(holder)") - byte doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + @Specialization(guards = "isNull(holder)") + byte doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().getByte(offset, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + byte doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { return UnsafeAccess.getIfAllowed(getMeta()).getByte(unwrapNullOrArray(getLanguage(), holder), offset); } @@ -1594,9 +1813,18 @@ private static byte doGet(StaticObject holder, Field f, Meta meta) { public abstract static class GetObjectWithBase extends UnsafeAccessNode { abstract @JavaType(Object.class) StaticObject execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset); - @Specialization(guards = "isNullOrArray(holder)") + @Specialization(guards = "isNull(holder)") + @JavaType(Object.class) + @SuppressWarnings("unused") + StaticObject doOffHeap(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, + long offset) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere("get*Object from native memory"); + } + + @Specialization(guards = "isArray(holder)") @JavaType(Object.class) - StaticObject doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, + StaticObject doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { return (StaticObject) UnsafeAccess.getIfAllowed(getMeta()).getObject(unwrapNullOrArray(getLanguage(), holder), offset); } @@ -1638,8 +1866,19 @@ private static StaticObject doGetField(StaticObject holder, Field f, Meta meta) public abstract static class GetBooleanWithBase extends UnsafeAccessNode { abstract boolean execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset); - @Specialization(guards = "isNullOrArray(holder)") - boolean doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + @Specialization(guards = "isNull(holder)") + boolean doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().getBoolean(offset, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + boolean doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { return UnsafeAccess.getIfAllowed(getMeta()).getBoolean(unwrapNullOrArray(getLanguage(), holder), offset); } @@ -1680,9 +1919,20 @@ private static boolean doGet(StaticObject holder, Field f, Meta meta) { public abstract static class GetCharWithBase extends UnsafeAccessNode { abstract char execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset); - @Specialization(guards = "isNullOrArray(holder)") - char doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { - return UnsafeAccess.getIfAllowed(getMeta()).getChar(unwrapNullOrArray(getLanguage(), holder), offset); + @Specialization(guards = "isNull(holder)") + char doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().getChar(offset, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + char doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return UnsafeAccess.getIfAllowed(getMeta()).getChar(unwrapArray(getLanguage(), holder), offset); } @Specialization(guards = "!isNullOrArray(holder)") @@ -1722,9 +1972,20 @@ private static char doGet(StaticObject holder, Field f, Meta meta) { public abstract static class GetShortWithBase extends UnsafeAccessNode { abstract short execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset); - @Specialization(guards = "isNullOrArray(holder)") - short doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { - return UnsafeAccess.getIfAllowed(getMeta()).getShort(unwrapNullOrArray(getLanguage(), holder), offset); + @Specialization(guards = "isNull(holder)") + short doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().getShort(offset, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + short doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return UnsafeAccess.getIfAllowed(getMeta()).getShort(unwrapArray(getLanguage(), holder), offset); } @Specialization(guards = "!isNullOrArray(holder)") @@ -1764,9 +2025,20 @@ private static short doGet(StaticObject holder, Field f, Meta meta) { public abstract static class GetIntWithBase extends UnsafeAccessNode { abstract int execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset); - @Specialization(guards = "isNullOrArray(holder)") - int doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { - return UnsafeAccess.getIfAllowed(getMeta()).getInt(unwrapNullOrArray(getLanguage(), holder), offset); + @Specialization(guards = "isNull(holder)") + int doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().getInt(offset, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + int doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return UnsafeAccess.getIfAllowed(getMeta()).getInt(unwrapArray(getLanguage(), holder), offset); } @Specialization(guards = "!isNullOrArray(holder)") @@ -1806,9 +2078,20 @@ private static int doGet(StaticObject holder, Field f, Meta meta) { public abstract static class GetFloatWithBase extends UnsafeAccessNode { abstract float execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset); - @Specialization(guards = "isNullOrArray(holder)") - float doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { - return UnsafeAccess.getIfAllowed(getMeta()).getFloat(unwrapNullOrArray(getLanguage(), holder), offset); + @Specialization(guards = "isNull(holder)") + float doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().getFloat(offset, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + float doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return UnsafeAccess.getIfAllowed(getMeta()).getFloat(unwrapArray(getLanguage(), holder), offset); } @Specialization(guards = "!isNullOrArray(holder)") @@ -1848,9 +2131,20 @@ private static float doGet(StaticObject holder, Field f, Meta meta) { public abstract static class GetDoubleWithBase extends UnsafeAccessNode { abstract double execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset); - @Specialization(guards = "isNullOrArray(holder)") - double doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { - return UnsafeAccess.getIfAllowed(getMeta()).getDouble(unwrapNullOrArray(getLanguage(), holder), offset); + @Specialization(guards = "isNull(holder)") + double doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().getDouble(offset, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + double doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return UnsafeAccess.getIfAllowed(getMeta()).getDouble(unwrapArray(getLanguage(), holder), offset); } @Specialization(guards = "!isNullOrArray(holder)") @@ -1890,9 +2184,20 @@ private static double doGet(StaticObject holder, Field f, Meta meta) { public abstract static class GetLongWithBase extends UnsafeAccessNode { abstract long execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset); - @Specialization(guards = "isNullOrArray(holder)") - long doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { - return UnsafeAccess.getIfAllowed(getMeta()).getLong(unwrapNullOrArray(getLanguage(), holder), offset); + @Specialization(guards = "isNull(holder)") + long doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().getLong(offset, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + long doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return UnsafeAccess.getIfAllowed(getMeta()).getLong(unwrapArray(getLanguage(), holder), offset); } @Specialization(guards = "!isNullOrArray(holder)") @@ -1936,9 +2241,20 @@ private static long doGet(StaticObject holder, Field f, Meta meta) { public abstract static class GetByteVolatileWithBase extends UnsafeAccessNode { abstract byte execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset); - @Specialization(guards = "isNullOrArray(holder)") - byte doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { - return UnsafeAccess.getIfAllowed(getMeta()).getByteVolatile(unwrapNullOrArray(getLanguage(), holder), offset); + @Specialization(guards = "isNull(holder)") + byte doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().getByte(offset, MemoryAccessMode.VOLATILE); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + byte doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return UnsafeAccess.getIfAllowed(getMeta()).getByteVolatile(unwrapArray(getLanguage(), holder), offset); } @Specialization(guards = "!isNullOrArray(holder)") @@ -1978,11 +2294,20 @@ private static byte doGet(StaticObject holder, Field f, Meta meta) { public abstract static class GetObjectVolatileWithBase extends UnsafeAccessNode { abstract @JavaType(Object.class) StaticObject execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset); - @Specialization(guards = "isNullOrArray(holder)") + @Specialization(guards = "isNull(holder)") @JavaType(Object.class) - StaticObject doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, + @SuppressWarnings("unused") + StaticObject doOffHeap(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { - return (StaticObject) UnsafeAccess.getIfAllowed(getMeta()).getObjectVolatile(unwrapNullOrArray(getLanguage(), holder), offset); + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere("get*Object from native memory"); + } + + @Specialization(guards = "isArray(holder)") + @JavaType(Object.class) + StaticObject doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, + long offset) { + return (StaticObject) UnsafeAccess.getIfAllowed(getMeta()).getObjectVolatile(unwrapArray(getLanguage(), holder), offset); } @Specialization(guards = "!isNullOrArray(holder)") @@ -2023,9 +2348,20 @@ private static StaticObject doGet(StaticObject holder, Field f, Meta meta) { public abstract static class GetBooleanVolatileWithBase extends UnsafeAccessNode { abstract boolean execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset); - @Specialization(guards = "isNullOrArray(holder)") - boolean doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { - return UnsafeAccess.getIfAllowed(getMeta()).getBooleanVolatile(unwrapNullOrArray(getLanguage(), holder), offset); + @Specialization(guards = "isNull(holder)") + boolean doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().getBoolean(offset, MemoryAccessMode.VOLATILE); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + boolean doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return UnsafeAccess.getIfAllowed(getMeta()).getBooleanVolatile(unwrapArray(getLanguage(), holder), offset); } @Specialization(guards = "!isNullOrArray(holder)") @@ -2065,9 +2401,20 @@ private static boolean doGet(StaticObject holder, Field f, Meta meta) { public abstract static class GetCharVolatileWithBase extends UnsafeAccessNode { abstract char execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset); - @Specialization(guards = "isNullOrArray(holder)") - char doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { - return UnsafeAccess.getIfAllowed(getMeta()).getCharVolatile(unwrapNullOrArray(getLanguage(), holder), offset); + @Specialization(guards = "isNull(holder)") + char doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().getChar(offset, MemoryAccessMode.VOLATILE); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + char doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return UnsafeAccess.getIfAllowed(getMeta()).getCharVolatile(unwrapArray(getLanguage(), holder), offset); } @Specialization(guards = "!isNullOrArray(holder)") @@ -2107,9 +2454,20 @@ private static char doGet(StaticObject holder, Field f, Meta meta) { public abstract static class GetShortVolatileWithBase extends UnsafeAccessNode { abstract short execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset); - @Specialization(guards = "isNullOrArray(holder)") - short doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { - return UnsafeAccess.getIfAllowed(getMeta()).getShortVolatile(unwrapNullOrArray(getLanguage(), holder), offset); + @Specialization(guards = "isNull(holder)") + short doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().getShort(offset, MemoryAccessMode.VOLATILE); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + short doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return UnsafeAccess.getIfAllowed(getMeta()).getShortVolatile(unwrapArray(getLanguage(), holder), offset); } @Specialization(guards = "!isNullOrArray(holder)") @@ -2149,9 +2507,20 @@ private static short doGet(StaticObject holder, Field f, Meta meta) { public abstract static class GetIntVolatileWithBase extends UnsafeAccessNode { abstract int execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset); - @Specialization(guards = "isNullOrArray(holder)") - int doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { - return UnsafeAccess.getIfAllowed(getMeta()).getIntVolatile(unwrapNullOrArray(getLanguage(), holder), offset); + @Specialization(guards = "isNull(holder)") + int doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().getInt(offset, MemoryAccessMode.VOLATILE); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + int doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return UnsafeAccess.getIfAllowed(getMeta()).getIntVolatile(unwrapArray(getLanguage(), holder), offset); } @Specialization(guards = "!isNullOrArray(holder)") @@ -2191,9 +2560,20 @@ private static int doGet(StaticObject holder, Field f, Meta meta) { public abstract static class GetFloatVolatileWithBase extends UnsafeAccessNode { abstract float execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset); - @Specialization(guards = "isNullOrArray(holder)") - float doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { - return UnsafeAccess.getIfAllowed(getMeta()).getFloatVolatile(unwrapNullOrArray(getLanguage(), holder), offset); + @Specialization(guards = "isNull(holder)") + float doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().getFloat(offset, MemoryAccessMode.VOLATILE); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + float doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return UnsafeAccess.getIfAllowed(getMeta()).getFloatVolatile(unwrapArray(getLanguage(), holder), offset); } @Specialization(guards = "!isNullOrArray(holder)") @@ -2233,9 +2613,20 @@ private static float doGet(StaticObject holder, Field f, Meta meta) { public abstract static class GetDoubleVolatileWithBase extends UnsafeAccessNode { abstract double execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset); - @Specialization(guards = "isNullOrArray(holder)") - double doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { - return UnsafeAccess.getIfAllowed(getMeta()).getDoubleVolatile(unwrapNullOrArray(getLanguage(), holder), offset); + @Specialization(guards = "isNull(holder)") + double doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().getDouble(offset, MemoryAccessMode.VOLATILE); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + double doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return UnsafeAccess.getIfAllowed(getMeta()).getDoubleVolatile(unwrapArray(getLanguage(), holder), offset); } @Specialization(guards = "!isNullOrArray(holder)") @@ -2275,9 +2666,20 @@ private static double doGet(StaticObject holder, Field f, Meta meta) { public abstract static class GetLongVolatileWithBase extends UnsafeAccessNode { abstract long execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset); - @Specialization(guards = "isNullOrArray(holder)") - long doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { - return UnsafeAccess.getIfAllowed(getMeta()).getLongVolatile(unwrapNullOrArray(getLanguage(), holder), offset); + @Specialization(guards = "isNull(holder)") + long doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().getLong(offset, MemoryAccessMode.VOLATILE); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + long doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return UnsafeAccess.getIfAllowed(getMeta()).getLongVolatile(unwrapArray(getLanguage(), holder), offset); } @Specialization(guards = "!isNullOrArray(holder)") @@ -2322,8 +2724,14 @@ abstract static class GetByte extends UnsafeAccessNode { abstract byte execute(@JavaType(Unsafe.class) StaticObject self, long address); @Specialization - byte doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address) { - return UnsafeAccess.getIfAllowed(getMeta()).getByte(address); + byte doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().getByte(address, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } } } @@ -2334,8 +2742,14 @@ abstract static class GetChar extends UnsafeAccessNode { abstract char execute(@JavaType(Unsafe.class) StaticObject self, long address); @Specialization - char doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address) { - return UnsafeAccess.getIfAllowed(getMeta()).getChar(address); + char doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().getChar(address, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } } } @@ -2346,12 +2760,20 @@ abstract static class GetShort extends UnsafeAccessNode { abstract short execute(@JavaType(Unsafe.class) StaticObject self, long address); @Specialization - short doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address) { - return UnsafeAccess.getIfAllowed(getMeta()).getShort(address); + short doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().getShort(address, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } } } - /** @see GetByteWithBase */ + /** + * @see GetByteWithBase + */ @GenerateInline(false) // not available in substitutions @Substitution(hasReceiver = true) abstract static class GetInt extends UnsafeAccessNode { @@ -2359,8 +2781,14 @@ abstract static class GetInt extends UnsafeAccessNode { abstract int execute(@JavaType(Unsafe.class) StaticObject self, long address); @Specialization - int doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address) { - return UnsafeAccess.getIfAllowed(getMeta()).getInt(address); + int doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().getInt(address, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } } } @@ -2371,8 +2799,14 @@ abstract static class GetFloat extends UnsafeAccessNode { abstract float execute(@JavaType(Unsafe.class) StaticObject self, long address); @Specialization - float doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address) { - return UnsafeAccess.getIfAllowed(getMeta()).getFloat(address); + float doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().getFloat(address, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } } } @@ -2383,8 +2817,14 @@ abstract static class GetDouble extends UnsafeAccessNode { abstract double execute(@JavaType(Unsafe.class) StaticObject self, long address); @Specialization - double doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address) { - return UnsafeAccess.getIfAllowed(getMeta()).getDouble(address); + double doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().getDouble(address, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } } } @@ -2395,8 +2835,14 @@ abstract static class GetLong extends UnsafeAccessNode { abstract long execute(@JavaType(Unsafe.class) StaticObject self, long address); @Specialization - long doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address) { - return UnsafeAccess.getIfAllowed(getMeta()).getLong(address); + long doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().getLong(address, MemoryAccessMode.PLAIN); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } } } @@ -2410,9 +2856,20 @@ long doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject s public abstract static class PutByteVolatileWithBase extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, byte value); - @Specialization(guards = "isNullOrArray(holder)") - void doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, byte value) { - UnsafeAccess.getIfAllowed(getMeta()).putByteVolatile(unwrapNullOrArray(getLanguage(), holder), offset, value); + @Specialization(guards = "isNull(holder)") + void doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, byte value, + @Cached InlinedBranchProfile errorBranch) { + try { + getNativeAccess().nativeMemory().putByte(offset, value, MemoryAccessMode.VOLATILE); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + void doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, byte value) { + UnsafeAccess.getIfAllowed(getMeta()).putByteVolatile(unwrapArray(getLanguage(), holder), offset, value); } @Specialization(guards = "!isNullOrArray(holder)") @@ -2450,10 +2907,18 @@ public abstract static class PutObjectVolatileWithBase extends UnsafeAccessNode abstract void execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, @JavaType(Object.class) StaticObject value); - @Specialization(guards = "isNullOrArray(holder)") - void doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, + @Specialization(guards = "isNull(holder)") + @SuppressWarnings("unused") + void doOffHeap(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, @JavaType(Object.class) StaticObject value) { - UnsafeAccess.getIfAllowed(getMeta()).putObjectVolatile(unwrapNullOrArray(getLanguage(), holder), offset, value); + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere("putObject* to native memory"); + } + + @Specialization(guards = "isArray(holder)") + void doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, + @JavaType(Object.class) StaticObject value) { + UnsafeAccess.getIfAllowed(getMeta()).putObjectVolatile(unwrapArray(getLanguage(), holder), offset, value); } @Specialization(guards = "!isNullOrArray(holder)") @@ -2491,9 +2956,20 @@ private static void doPut(Field f, StaticObject holder, StaticObject value, Meta public abstract static class PutBooleanVolatileWithBase extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, boolean value); - @Specialization(guards = "isNullOrArray(holder)") - void doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, boolean value) { - UnsafeAccess.getIfAllowed(getMeta()).putBooleanVolatile(unwrapNullOrArray(getLanguage(), holder), offset, value); + @Specialization(guards = "isNull(holder)") + void doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, boolean value, + @Cached InlinedBranchProfile errorBranch) { + try { + getNativeAccess().nativeMemory().putBoolean(offset, value, MemoryAccessMode.VOLATILE); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + void doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, boolean value) { + UnsafeAccess.getIfAllowed(getMeta()).putBooleanVolatile(unwrapArray(getLanguage(), holder), offset, value); } @Specialization(guards = "!isNullOrArray(holder)") @@ -2530,9 +3006,20 @@ private static void doPut(Field f, StaticObject holder, boolean value, Meta meta public abstract static class PutCharVolatileWithBase extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, char value); - @Specialization(guards = "isNullOrArray(holder)") - void doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, char value) { - UnsafeAccess.getIfAllowed(getMeta()).putCharVolatile(unwrapNullOrArray(getLanguage(), holder), offset, value); + @Specialization(guards = "isNull(holder)") + void doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, char value, + @Cached InlinedBranchProfile errorBranch) { + try { + getNativeAccess().nativeMemory().putChar(offset, value, MemoryAccessMode.VOLATILE); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + void doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, char value) { + UnsafeAccess.getIfAllowed(getMeta()).putCharVolatile(unwrapArray(getLanguage(), holder), offset, value); } @Specialization(guards = "!isNullOrArray(holder)") @@ -2569,9 +3056,20 @@ private static void doPut(Field f, StaticObject holder, char value, Meta meta) { public abstract static class PutShortVolatileWithBase extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, short value); - @Specialization(guards = "isNullOrArray(holder)") - void doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, short value) { - UnsafeAccess.getIfAllowed(getMeta()).putShortVolatile(unwrapNullOrArray(getLanguage(), holder), offset, value); + @Specialization(guards = "isNull(holder)") + void doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, short value, + @Cached InlinedBranchProfile errorBranch) { + try { + getNativeAccess().nativeMemory().putShort(offset, value, MemoryAccessMode.VOLATILE); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + void doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, short value) { + UnsafeAccess.getIfAllowed(getMeta()).putShortVolatile(unwrapArray(getLanguage(), holder), offset, value); } @Specialization(guards = "!isNullOrArray(holder)") @@ -2608,9 +3106,20 @@ private static void doPut(Field f, StaticObject holder, short value, Meta meta) public abstract static class PutIntVolatileWithBase extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, int value); - @Specialization(guards = "isNullOrArray(holder)") - void doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, int value) { - UnsafeAccess.getIfAllowed(getMeta()).putIntVolatile(unwrapNullOrArray(getLanguage(), holder), offset, value); + @Specialization(guards = "isNull(holder)") + void doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, int value, + @Cached InlinedBranchProfile errorBranch) { + try { + getNativeAccess().nativeMemory().putInt(offset, value, MemoryAccessMode.VOLATILE); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + void doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, int value) { + UnsafeAccess.getIfAllowed(getMeta()).putIntVolatile(unwrapArray(getLanguage(), holder), offset, value); } @Specialization(guards = "!isNullOrArray(holder)") @@ -2647,9 +3156,20 @@ private static void doPut(Field f, StaticObject holder, int value, Meta meta) { public abstract static class PutFloatVolatileWithBase extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, float value); - @Specialization(guards = "isNullOrArray(holder)") - void doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, float value) { - UnsafeAccess.getIfAllowed(getMeta()).putFloatVolatile(unwrapNullOrArray(getLanguage(), holder), offset, value); + @Specialization(guards = "isNull(holder)") + void doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, float value, + @Cached InlinedBranchProfile errorBranch) { + try { + getNativeAccess().nativeMemory().putFloat(offset, value, MemoryAccessMode.VOLATILE); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + void doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, float value) { + UnsafeAccess.getIfAllowed(getMeta()).putFloatVolatile(unwrapArray(getLanguage(), holder), offset, value); } @Specialization(guards = "!isNullOrArray(holder)") @@ -2686,9 +3206,20 @@ private static void doPut(Field f, StaticObject holder, float value, Meta meta) public abstract static class PutDoubleVolatileWithBase extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, double value); - @Specialization(guards = "isNullOrArray(holder)") - void doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, double value) { - UnsafeAccess.getIfAllowed(getMeta()).putDoubleVolatile(unwrapNullOrArray(getLanguage(), holder), offset, value); + @Specialization(guards = "isNull(holder)") + void doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, double value, + @Cached InlinedBranchProfile errorBranch) { + try { + getNativeAccess().nativeMemory().putDouble(offset, value, MemoryAccessMode.VOLATILE); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + void doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, double value) { + UnsafeAccess.getIfAllowed(getMeta()).putDoubleVolatile(unwrapArray(getLanguage(), holder), offset, value); } @Specialization(guards = "!isNullOrArray(holder)") @@ -2725,9 +3256,20 @@ private static void doPut(Field f, StaticObject holder, double value, Meta meta) public abstract static class PutLongVolatileWithBase extends UnsafeAccessNode { abstract void execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, long value); - @Specialization(guards = "isNullOrArray(holder)") - void doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, long value) { - UnsafeAccess.getIfAllowed(getMeta()).putLongVolatile(unwrapNullOrArray(getLanguage(), holder), offset, value); + @Specialization(guards = "isNull(holder)") + void doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, long value, + @Cached InlinedBranchProfile errorBranch) { + try { + getNativeAccess().nativeMemory().putLong(offset, value, MemoryAccessMode.VOLATILE); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + void doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, long value) { + UnsafeAccess.getIfAllowed(getMeta()).putLongVolatile(unwrapArray(getLanguage(), holder), offset, value); } @Specialization(guards = "!isNullOrArray(holder)") @@ -2769,10 +3311,18 @@ public abstract static class CompareAndSwapObject extends UnsafeAccessNode { abstract boolean execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, @JavaType(Object.class) StaticObject before, @JavaType(Object.class) StaticObject after); - @Specialization(guards = "isNullOrArray(holder)") - boolean doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, + @Specialization(guards = "isNull(holder)") + @SuppressWarnings("unused") + boolean doOffHeap(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, @JavaType(Object.class) StaticObject before, @JavaType(Object.class) StaticObject after) { - return UnsafeAccess.getIfAllowed(getMeta()).compareAndSwapObject(unwrapNullOrArray(getLanguage(), holder), offset, before, after); + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere("CAS reference to native memory"); + } + + @Specialization(guards = "isArray(holder)") + boolean doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, + @JavaType(Object.class) StaticObject before, @JavaType(Object.class) StaticObject after) { + return UnsafeAccess.getIfAllowed(getMeta()).compareAndSwapObject(unwrapArray(getLanguage(), holder), offset, before, after); } @Specialization(guards = "!isNullOrArray(holder)") @@ -2811,10 +3361,22 @@ public abstract static class CompareAndSwapInt extends UnsafeAccessNode { abstract boolean execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, int before, int after); - @Specialization(guards = "isNullOrArray(holder)") - boolean doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, + @Specialization(guards = "isNull(holder)") + boolean doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, + int before, int after, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().compareAndSetInt(offset, before, after); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + boolean doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, int before, int after) { - return UnsafeAccess.getIfAllowed(getMeta()).compareAndSwapInt(unwrapNullOrArray(getLanguage(), holder), offset, before, after); + return UnsafeAccess.getIfAllowed(getMeta()).compareAndSwapInt(unwrapArray(getLanguage(), holder), offset, before, after); } @Specialization(guards = "!isNullOrArray(holder)") @@ -2861,10 +3423,22 @@ public abstract static class CompareAndSwapLong extends UnsafeAccessNode { abstract boolean execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, long before, long after); - @Specialization(guards = "isNullOrArray(holder)") - boolean doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, + @Specialization(guards = "isNull(holder)") + boolean doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, + long before, long after, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().compareAndSetLong(offset, before, after); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + boolean doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, long before, long after) { - return UnsafeAccess.getIfAllowed(getMeta()).compareAndSwapLong(unwrapNullOrArray(getLanguage(), holder), offset, before, after); + return UnsafeAccess.getIfAllowed(getMeta()).compareAndSwapLong(unwrapArray(getLanguage(), holder), offset, before, after); } @Specialization(guards = "!isNullOrArray(holder)") @@ -2926,13 +3500,23 @@ public abstract static class CompareAndExchangeObject extends UnsafeAccessNode { abstract @JavaType(Object.class) StaticObject execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, @JavaType(Object.class) StaticObject before, @JavaType(Object.class) StaticObject after); - @Specialization(guards = "isNullOrArray(holder)") + @Specialization(guards = "isNull(holder)") + @JavaType(Object.class) + @SuppressWarnings("unused") + StaticObject doOffHeap(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, + long offset, + @JavaType(Object.class) StaticObject before, @JavaType(Object.class) StaticObject after) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere("CAS reference on native memory"); + } + + @Specialization(guards = "isArray(holder)") @JavaType(Object.class) - StaticObject doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, + StaticObject doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, @JavaType(Object.class) StaticObject before, @JavaType(Object.class) StaticObject after) { UnsafeAccess.checkAllowed(getMeta()); - return (StaticObject) UnsafeSupport.compareAndExchangeObject(unwrapNullOrArray(getLanguage(), holder), offset, before, after); + return (StaticObject) UnsafeSupport.compareAndExchangeObject(unwrapArray(getLanguage(), holder), offset, before, after); } @Specialization(guards = "!isNullOrArray(holder)") @@ -2976,11 +3560,22 @@ public abstract static class CompareAndExchangeInt extends UnsafeAccessNode { abstract int execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, int before, int after); - @Specialization(guards = "isNullOrArray(holder)") - int doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, + @Specialization(guards = "isNull(holder)") + int doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, + int before, int after, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().compareAndExchangeInt(offset, before, after); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + int doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, int before, int after) { - UnsafeAccess.checkAllowed(getMeta()); - return UnsafeSupport.compareAndExchangeInt(unwrapNullOrArray(getLanguage(), holder), offset, before, after); + return UnsafeSupport.compareAndExchangeInt(unwrapArray(getLanguage(), holder), offset, before, after); } @Specialization(guards = "!isNullOrArray(holder)") @@ -3026,11 +3621,29 @@ public abstract static class CompareAndExchangeByte extends UnsafeAccessNode { abstract byte execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, byte before, byte after); - @Specialization(guards = "isNullOrArray(holder)") - byte doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, + @Specialization(guards = "isNull(holder)") + byte doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, + byte before, byte after, + @Cached InlinedBranchProfile errorBranch) { + try { + return UnsafeSupport.compareAndExchangeByte(holder, offset, before, after, getNativeAccess().nativeMemory()); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + byte doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, byte before, byte after) { UnsafeAccess.checkAllowed(getMeta()); - return UnsafeSupport.compareAndExchangeByte(unwrapNullOrArray(getLanguage(), holder), offset, before, after); + try { + return UnsafeSupport.compareAndExchangeByte(unwrapArray(getLanguage(), holder), offset, before, after, null); + } catch (IllegalMemoryAccessException e) { + // we should not reach here as we are not accessing native memory but arrays + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere(e); + } } @Specialization(guards = "!isNullOrArray(holder)") @@ -3076,11 +3689,29 @@ public abstract static class CompareAndExchangeShort extends UnsafeAccessNode { abstract short execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, short before, short after); - @Specialization(guards = "isNullOrArray(holder)") - short doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, + @Specialization(guards = "isNull(holder)") + short doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, + short before, short after, + @Cached InlinedBranchProfile errorBranch) { + try { + return UnsafeSupport.compareAndExchangeShort(holder, offset, before, after, getNativeAccess().nativeMemory()); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + short doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, short before, short after) { UnsafeAccess.checkAllowed(getMeta()); - return UnsafeSupport.compareAndExchangeShort(unwrapNullOrArray(getLanguage(), holder), offset, before, after); + try { + return UnsafeSupport.compareAndExchangeShort(unwrapArray(getLanguage(), holder), offset, before, after, null); + } catch (IllegalMemoryAccessException e) { + // we should not reach here as we are not accessing native memory but arrays + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere(e); + } } @Specialization(guards = "!isNullOrArray(holder)") @@ -3126,11 +3757,23 @@ public abstract static class CompareAndExchangeLong extends UnsafeAccessNode { abstract long execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, long before, long after); - @Specialization(guards = "isNullOrArray(holder)") - long doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, + @Specialization(guards = "isNull(holder)") + long doOffHeap(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @SuppressWarnings("unused") @JavaType(Object.class) StaticObject holder, long offset, + long before, long after, + @Cached InlinedBranchProfile errorBranch) { + try { + return getNativeAccess().nativeMemory().compareAndExchangeLong(offset, before, after); + } catch (IllegalMemoryAccessException e) { + errorBranch.enter(this); + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary("Invalid memory access: address refers to memory outside the allocated region"); + } + } + + @Specialization(guards = "isArray(holder)") + long doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, long before, long after) { UnsafeAccess.checkAllowed(getMeta()); - return UnsafeSupport.compareAndExchangeLong(unwrapNullOrArray(getLanguage(), holder), offset, before, after); + return UnsafeSupport.compareAndExchangeLong(unwrapArray(getLanguage(), holder), offset, before, after); } @Specialization(guards = "!isNullOrArray(holder)") @@ -3181,9 +3824,19 @@ public abstract static class GetAndSetObject extends UnsafeAccessNode { abstract @JavaType(Unsafe.class) StaticObject execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, @JavaType(Object.class) StaticObject value); - @Specialization(guards = "isNullOrArray(holder)") + @Specialization(guards = "isNull(holder)") + @JavaType(Unsafe.class) + @SuppressWarnings("unused") + StaticObject doOffHeap(@JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, + long offset, + @JavaType(Object.class) StaticObject value) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere("Object* to native memory"); + } + + @Specialization(guards = "isArray(holder)") @JavaType(Unsafe.class) - StaticObject doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, + StaticObject doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, @JavaType(Object.class) StaticObject value) { return (StaticObject) UnsafeAccess.getIfAllowed(getMeta()).getAndSetObject(unwrapNullOrArray(getLanguage(), holder), offset, value); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/UnsafeSupport.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/UnsafeSupport.java index 701b5ab33c09..668e611fe4bb 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/UnsafeSupport.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/UnsafeSupport.java @@ -22,9 +22,13 @@ */ package com.oracle.truffle.espresso.substitutions.standard; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.IllegalMemoryAccessException; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.MemoryAccessMode; + import java.nio.ByteOrder; import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; import com.oracle.truffle.espresso.meta.EspressoError; import sun.misc.Unsafe; @@ -58,58 +62,10 @@ static boolean isBigEndian() { return ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN; } - static boolean compareAndSetByte( - Object o, long offset, - byte expected, - byte x) { - return compareAndExchangeByte(o, offset, expected, x) == expected; - } - - static boolean compareAndSetBoolean( - Object o, long offset, - boolean expected, - boolean x) { - byte byteExpected = expected ? (byte) 1 : (byte) 0; - byte byteX = x ? (byte) 1 : (byte) 0; - return compareAndSetByte(o, offset, byteExpected, byteX); - } - - static boolean compareAndSetShort( - Object o, long offset, - short expected, - short x) { - return compareAndExchangeShort(o, offset, expected, x) == expected; - } - - static boolean compareAndSetChar( - Object o, long offset, - char expected, - char x) { - return compareAndSetShort(o, offset, (short) expected, (short) x); - } - - static boolean compareAndSetFloat( - Object o, long offset, - float expected, - float x) { - return UNSAFE.compareAndSwapInt(o, offset, - Float.floatToRawIntBits(expected), - Float.floatToRawIntBits(x)); - } - - static boolean compareAndSetDouble( - Object o, long offset, - double expected, - double x) { - return UNSAFE.compareAndSwapLong(o, offset, - Double.doubleToRawLongBits(expected), - Double.doubleToRawLongBits(x)); - } - static byte compareAndExchangeByte( Object o, long offset, byte expected, - byte x) { + byte x, NativeMemory nativeMemory) throws IllegalMemoryAccessException { long wordOffset = offset & ~3; int shift = (int) (offset & 3) << 3; if (isBigEndian()) { @@ -120,28 +76,33 @@ static byte compareAndExchangeByte( int maskedX = (x & 0xFF) << shift; int fullWord; do { - fullWord = UNSAFE.getIntVolatile(o, wordOffset); + fullWord = doGetIntVolatile(o, wordOffset, nativeMemory); if ((fullWord & mask) != maskedExpected) { return (byte) ((fullWord & mask) >> shift); } - } while (!UNSAFE.compareAndSwapInt(o, wordOffset, - fullWord, (fullWord & ~mask) | maskedX)); + } while (!doCAS(o, wordOffset, + fullWord, (fullWord & ~mask) | maskedX, nativeMemory)); return expected; } - static boolean compareAndExchangeBoolean( - Object o, long offset, - boolean expected, - boolean x) { - byte byteExpected = expected ? (byte) 1 : (byte) 0; - byte byteX = x ? (byte) 1 : (byte) 0; - return compareAndExchangeByte(o, offset, byteExpected, byteX) != 0; + private static int doGetIntVolatile(Object o, long offset, NativeMemory nativeMemory) throws IllegalMemoryAccessException { + if (nativeMemory != null && o == null) { + return nativeMemory.getInt(offset, MemoryAccessMode.VOLATILE); + } + return UNSAFE.getIntVolatile(o, offset); + } + + private static boolean doCAS(Object o, long offset, int expected, int value, NativeMemory nativeMemory) throws IllegalMemoryAccessException { + if (nativeMemory != null && o == null) { + return nativeMemory.compareAndExchangeInt(offset, expected, value) == expected; + } + return UNSAFE.compareAndSwapInt(o, offset, expected, value); } static short compareAndExchangeShort( Object o, long offset, short expected, - short x) { + short x, NativeMemory nativeMemory) throws IllegalMemoryAccessException { if ((offset & 3) == 3) { throw new IllegalArgumentException("Update spans the word, not supported"); } @@ -155,22 +116,15 @@ static short compareAndExchangeShort( int maskedX = (x & 0xFFFF) << shift; int fullWord; do { - fullWord = UNSAFE.getIntVolatile(o, wordOffset); + fullWord = doGetIntVolatile(o, wordOffset, nativeMemory); if ((fullWord & mask) != maskedExpected) { return (short) ((fullWord & mask) >> shift); } - } while (!UNSAFE.compareAndSwapInt(o, wordOffset, - fullWord, (fullWord & ~mask) | maskedX)); + } while (!doCAS(o, wordOffset, + fullWord, (fullWord & ~mask) | maskedX, nativeMemory)); return expected; } - static char compareAndExchangeChar( - Object o, long offset, - char expected, - char x) { - return (char) compareAndExchangeShort(o, offset, (short) expected, (short) x); - } - static int compareAndExchangeInt( Object o, long offset, int expected, @@ -199,15 +153,6 @@ static Object compareAndExchangeObject( return expected; } - static float compareAndExchangeFloat( - Object o, long offset, - float expected, - float x) { - return Float.intBitsToFloat(compareAndExchangeInt(o, offset, - Float.floatToRawIntBits(expected), - Float.floatToRawIntBits(x))); - } - static long compareAndExchangeLong( Object o, long offset, long expected, @@ -222,25 +167,16 @@ static long compareAndExchangeLong( return expected; } - static double compareAndExchangeDouble( - Object o, long offset, - double expected, - double x) { - return Double.longBitsToDouble(compareAndExchangeLong(o, offset, - Double.doubleToRawLongBits(expected), - Double.doubleToRawLongBits(x))); - } - - static void copySwapMemory(Object src, long srcOffset, Object dst, long destOffset, long bytes, long elemSize) { + static void copySwapMemory(Object src, long srcOffset, Object dst, long destOffset, long bytes, long elemSize, NativeMemory nativeMemory) throws IllegalMemoryAccessException { switch ((int) elemSize) { case 2: - CSMHelper.do2(src, srcOffset, dst, destOffset, bytes, elemSize); + CSMHelper.do2(src, srcOffset, dst, destOffset, bytes, elemSize, nativeMemory); return; case 4: - CSMHelper.do4(src, srcOffset, dst, destOffset, bytes, elemSize); + CSMHelper.do4(src, srcOffset, dst, destOffset, bytes, elemSize, nativeMemory); return; case 8: - CSMHelper.do8(src, srcOffset, dst, destOffset, bytes, elemSize); + CSMHelper.do8(src, srcOffset, dst, destOffset, bytes, elemSize, nativeMemory); return; default: CompilerDirectives.transferToInterpreterAndInvalidate(); @@ -288,7 +224,7 @@ private static long swap(long l) { return Long.reverseBytes(l); } - static void do2(Object src, long srcOffset, Object dst, long destOffset, long bytes, long elemSize) { + static void do2(Object src, long srcOffset, Object dst, long destOffset, long bytes, long elemSize, NativeMemory nativeMemory) throws IllegalMemoryAccessException { assert elemSize == 2 : elemSize; Direction d = getDirection(src, srcOffset, dst, destOffset, bytes); char tmp; @@ -298,16 +234,31 @@ static void do2(Object src, long srcOffset, Object dst, long destOffset, long by while (copied < bytes) { - tmp = UNSAFE.getChar(src, srcOffset + pos); + tmp = doGetChar(src, srcOffset + pos, nativeMemory); tmp = swap(tmp); - UNSAFE.putChar(dst, destOffset + pos, tmp); + doPutChar(dst, destOffset + pos, tmp, nativeMemory); pos += d.inc(elemSize); copied += elemSize; } } - static void do4(Object src, long srcOffset, Object dst, long destOffset, long bytes, long elemSize) { + private static char doGetChar(Object o, long offSet, NativeMemory nativeMemory) throws IllegalMemoryAccessException { + if (o == null) { + return nativeMemory.getChar(offSet, MemoryAccessMode.PLAIN); + } + return UNSAFE.getChar(o, offSet); + } + + private static void doPutChar(Object o, long offSet, char x, NativeMemory nativeMemory) throws IllegalMemoryAccessException { + if (o == null) { + nativeMemory.putChar(offSet, x, MemoryAccessMode.PLAIN); + return; + } + UNSAFE.putChar(o, offSet, x); + } + + static void do4(Object src, long srcOffset, Object dst, long destOffset, long bytes, long elemSize, NativeMemory nativeMemory) throws IllegalMemoryAccessException { assert elemSize == 4 : elemSize; Direction d = getDirection(src, srcOffset, dst, destOffset, bytes); int tmp; @@ -317,15 +268,30 @@ static void do4(Object src, long srcOffset, Object dst, long destOffset, long by while (copied < bytes) { - tmp = swap(UNSAFE.getInt(src, srcOffset + pos)); - UNSAFE.putInt(dst, destOffset + pos, tmp); + tmp = swap(doGetInt(src, srcOffset + pos, nativeMemory)); + doPutInt(dst, destOffset + pos, tmp, nativeMemory); pos += d.inc(elemSize); copied += elemSize; } } - static void do8(Object src, long srcOffset, Object dst, long destOffset, long bytes, long elemSize) { + private static int doGetInt(Object o, long offSet, NativeMemory nativeMemory) throws IllegalMemoryAccessException { + if (o == null) { + return nativeMemory.getInt(offSet, MemoryAccessMode.PLAIN); + } + return UNSAFE.getInt(o, offSet); + } + + private static void doPutInt(Object o, long offSet, int x, NativeMemory nativeMemory) throws IllegalMemoryAccessException { + if (o == null) { + nativeMemory.putInt(offSet, x, MemoryAccessMode.PLAIN); + return; + } + UNSAFE.putInt(o, offSet, x); + } + + static void do8(Object src, long srcOffset, Object dst, long destOffset, long bytes, long elemSize, NativeMemory nativeMemory) throws IllegalMemoryAccessException { assert elemSize == 8 : elemSize; Direction d = getDirection(src, srcOffset, dst, destOffset, bytes); long tmp; @@ -335,124 +301,27 @@ static void do8(Object src, long srcOffset, Object dst, long destOffset, long by while (copied < bytes) { - tmp = swap(UNSAFE.getLong(src, srcOffset + pos)); - UNSAFE.putLong(dst, destOffset + pos, tmp); + tmp = swap(doGetLong(src, srcOffset + pos, nativeMemory)); + doPutLong(dst, destOffset + pos, tmp, nativeMemory); pos += d.inc(elemSize); copied += elemSize; } } - } - // For 11: - /*- - static boolean isBigEndian() { - return UNSAFE.isBigEndian(); - } - - static boolean compareAndSetByte( - Object o, long offset, - byte expected, - byte x) { - return UNSAFE.compareAndSetByte(o, offset, expected, x); - } - - static boolean compareAndSetBoolean( - Object o, long offset, - boolean expected, - boolean x) { - return UNSAFE.compareAndSetBoolean(o, offset, expected, x); - } - - static boolean compareAndSetShort( - Object o, long offset, - short expected, - short x) { - return UNSAFE.compareAndSetShort(o, offset, expected, x); - } - - static boolean compareAndSetChar( - Object o, long offset, - char expected, - char x) { - return UNSAFE.compareAndSetChar(o, offset, expected, x); - } - - static boolean compareAndSetFloat( - Object o, long offset, - float expected, - float x) { - return UNSAFE.compareAndSetFloat(o, offset, expected, x); - } - - static boolean compareAndSetDouble( - Object o, long offset, - double expected, - double x) { - return UNSAFE.compareAndSetDouble(o, offset, expected, x); - } - - static byte compareAndExchangeByte( - Object o, long offset, - byte expected, - byte x) { - return UNSAFE.compareAndExchangeByte(o, offset, expected, x); - } - - static boolean compareAndExchangeBoolean( - Object o, long offset, - boolean expected, - boolean x) { - return UNSAFE.compareAndExchangeBoolean(o, offset, expected, x); - } - - static short compareAndExchangeShort( - Object o, long offset, - short expected, - short x) { - return UNSAFE.compareAndExchangeShort(o, offset, expected, x); - } - - static char compareAndExchangeChar( - Object o, long offset, - char expected, - char x) { - return UNSAFE.compareAndExchangeChar(o, offset, expected, x); - } - - static int compareAndExchangeInt( - Object o, long offset, - int expected, - int x) { - return UNSAFE.compareAndExchangeInt(o, offset, expected, x); - } - - static Object compareAndExchangeObject( - Object o, long offset, - Object expected, - Object x) { - return UNSAFE.compareAndExchangeObject(o, offset, expected, x); - } - - static float compareAndExchangeFloat( - Object o, long offset, - float expected, - float x) { - return UNSAFE.compareAndExchangeFloat(o, offset, expected, x); - } - - static long compareAndExchangeLong( - Object o, long offset, - long expected, - long x) { - return UNSAFE.compareAndExchangeLong(o, offset, expected, x); - } - - static double compareAndExchangeDouble( - Object o, long offset, - double expected, - double x) { - return UNSAFE.compareAndExchangeDouble(o, offset, expected, x); + private static long doGetLong(Object o, long offSet, NativeMemory nativeMemory) throws IllegalMemoryAccessException { + if (o == null) { + return nativeMemory.getLong(offSet, MemoryAccessMode.PLAIN); + } + return UNSAFE.getLong(o, offSet); + } + + private static void doPutLong(Object o, long offSet, long x, NativeMemory nativeMemory) throws IllegalMemoryAccessException { + if (o == null) { + nativeMemory.putLong(offSet, x, MemoryAccessMode.PLAIN); + return; + } + UNSAFE.putLong(o, offSet, x); + } } - */ } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/Management.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/Management.java index 58bc348a9a4d..981b990c9b87 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/Management.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/Management.java @@ -22,6 +22,7 @@ */ package com.oracle.truffle.espresso.vm; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.IllegalMemoryAccessException; import static com.oracle.truffle.espresso.jni.JniEnv.JNI_OK; import java.lang.management.GarbageCollectorMXBean; @@ -350,9 +351,14 @@ public int GetOptionalSupport(@Pointer TruffleObject /* jmmOptionalSupport **/ s if (getUncached().isNull(supportPtr)) { return -1; } - ByteBuffer supportBuf = NativeUtils.directByteBuffer(supportPtr, 8); + ByteBuffer supportBuf; + try { + supportBuf = NativeUtils.wrapNativeMemory(supportPtr, 8, getNativeAccess().nativeMemory()); + } catch (IllegalMemoryAccessException e) { + return -1; + } supportBuf.putInt(0); // clear - JmmOptionalSupport.JmmOptionalSupportWrapper optionalSupport = getVM().getStructs().jmmOptionalSupport.wrap(getHandles(), supportPtr); + JmmOptionalSupport.JmmOptionalSupportWrapper optionalSupport = getVM().getStructs().jmmOptionalSupport.wrap(getHandles(), getNativeAccess().nativeMemory(), supportPtr); OptionalSupportRecord record = getOptionalSupportRecord(); if (record.compTimeMonitoringSupport) { @@ -810,8 +816,8 @@ public int GetLongAttributes(@SuppressWarnings("unused") @JavaType(Object.class) int count, /* long* */ @Pointer TruffleObject result) { int numAtts = 0; - ByteBuffer attsBuffer = NativeUtils.directByteBuffer(atts, count, JavaKind.Int); - ByteBuffer resBuffer = NativeUtils.directByteBuffer(result, count, JavaKind.Long); + ByteBuffer attsBuffer = NativeUtils.wrapNativeMemoryOrThrow(atts, count, JavaKind.Int, getNativeAccess().nativeMemory(), getMeta()); + ByteBuffer resBuffer = NativeUtils.wrapNativeMemoryOrThrow(result, count, JavaKind.Long, getNativeAccess().nativeMemory(), getMeta()); for (int i = 0; i < count; i++) { int att = attsBuffer.getInt(); long res = GetLongAttribute(obj, att); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/VM.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/VM.java index d4ef1719fa12..060cbc55c23e 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/VM.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/VM.java @@ -26,6 +26,7 @@ import static com.oracle.truffle.espresso.classfile.Constants.ACC_FINAL; import static com.oracle.truffle.espresso.classfile.Constants.ACC_LAMBDA_FORM_COMPILED; import static com.oracle.truffle.espresso.classfile.Constants.ACC_PUBLIC; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.IllegalMemoryAccessException; import static com.oracle.truffle.espresso.jni.JniEnv.JNI_EDETACHED; import static com.oracle.truffle.espresso.jni.JniEnv.JNI_ERR; import static com.oracle.truffle.espresso.jni.JniEnv.JNI_EVERSION; @@ -1540,10 +1541,15 @@ private int attachCurrentThread(@SuppressWarnings("unused") @Pointer TruffleObje if (InteropLibrary.getUncached().isNull(argsPtr)) { getLogger().fine("AttachCurrentThread with null args"); } else { - JavaVMAttachArgs.JavaVMAttachArgsWrapper attachArgs = getStructs().javaVMAttachArgs.wrap(getHandles(), argsPtr); + JavaVMAttachArgs.JavaVMAttachArgsWrapper attachArgs = getStructs().javaVMAttachArgs.wrap(getHandles(), getNativeAccess().nativeMemory(), argsPtr); if (JVM_IsSupportedJNIVersion(attachArgs.version())) { group = attachArgs.group(); - name = NativeUtils.fromUTF8Ptr(attachArgs.name()); + try { + name = NativeUtils.interopPointerToString(attachArgs.name(), getNativeAccess().nativeMemory()); + } catch (IllegalMemoryAccessException e) { + getLogger().warning("attachArgs does not point to a valid memory region: " + e); + return JNI_ERR; + } } else { getLogger().warning(String.format("AttachCurrentThread with unsupported JavaVMAttachArgs version: 0x%08x", attachArgs.version())); } @@ -1552,7 +1558,12 @@ private int attachCurrentThread(@SuppressWarnings("unused") @Pointer TruffleObje if (daemon) { getContext().getThreadAccess().setDaemon(thread, true); } - NativeUtils.writeToPointerPointer(getUncached(), penvPtr, jniEnv.getNativePointer()); + try { + NativeUtils.writeToPointerPointer(getUncached(), penvPtr, jniEnv.getNativePointer(), getNativeAccess().nativeMemory()); + } catch (IllegalMemoryAccessException e) { + getLogger().warning("penvPtr does not point to a valid memory region: " + e); + return JNI_ERR; + } return JNI_OK; } @@ -1672,7 +1683,12 @@ public int GetEnv(@Pointer TruffleObject vmPtr_, @Pointer TruffleObject envPtr, interopPtr = jniEnv.getNativePointer(); } if (interopPtr != null) { - NativeUtils.writeToPointerPointer(getUncached(), envPtr, interopPtr); + try { + NativeUtils.writeToPointerPointer(getUncached(), envPtr, interopPtr, getNativeAccess().nativeMemory()); + } catch (IllegalMemoryAccessException e) { + getLogger().warning("envPtr does not point to a valid memory region: " + e); + return JNI_ERR; + } return JNI_OK; } return JNI_EVERSION; @@ -1996,7 +2012,7 @@ public long JVM_ConstantPoolGetLongAt(@SuppressWarnings("unused") @JavaType(Obje // region class loading private Symbol namePtrToInternal(TruffleObject namePtr) { - String name = NativeUtils.interopPointerToString(namePtr); + String name = NativeUtils.interopPointerToStringOrThrow(namePtr, getNativeAccess().nativeMemory(), getMeta()); return nameToInternal(name); } @@ -2032,10 +2048,9 @@ public Symbol nameToInternal(String name) { throw getMeta().throwExceptionWithMessage(getMeta().java_lang_InternalError, "Lookup class is null"); } assert !getUncached().isNull(bufPtr); - ByteBuffer buf = NativeUtils.directByteBuffer(bufPtr, len, JavaKind.Byte); final byte[] bytes = new byte[len]; + ByteBuffer buf = NativeUtils.wrapNativeMemoryOrThrow(bufPtr, len, JavaKind.Byte, getNativeAccess().nativeMemory(), getMeta()); buf.get(bytes); - return lookupDefineClass(lookup, type, bytes, pd, initialize, flags, classData); } @@ -2101,10 +2116,9 @@ public StaticObject lookupDefineClass( @JavaType(ClassLoader.class) StaticObject loader, @Pointer TruffleObject bufPtr, int len, @JavaType(internalName = "Ljava/security/ProtectionDomain;") StaticObject pd) { - ByteBuffer buf = NativeUtils.directByteBuffer(bufPtr, len, JavaKind.Byte); final byte[] bytes = new byte[len]; + ByteBuffer buf = NativeUtils.wrapNativeMemoryOrThrow(bufPtr, len, JavaKind.Byte, getNativeAccess().nativeMemory(), getMeta()); buf.get(bytes); - Symbol type = namePtrToInternal(namePtr); // can be null return defineClass(type, loader, pd, bytes); } @@ -2156,8 +2170,12 @@ public StaticObject defineClass(Symbol type, StaticObject loader, StaticOb @VmImpl(isJni = true) @TruffleBoundary public @JavaType(Class.class) StaticObject JVM_FindClassFromBootLoader(@Pointer TruffleObject namePtr) { - String name = NativeUtils.interopPointerToString(namePtr); - return findClassFromBootLoader(name); + try { + String name = NativeUtils.interopPointerToString(namePtr, getNativeAccess().nativeMemory()); + return findClassFromBootLoader(name); + } catch (IllegalMemoryAccessException e) { + return StaticObject.NULL; + } } public StaticObject findClassFromBootLoader(String name) { @@ -2227,7 +2245,7 @@ public StaticObject findClassFromCaller(Symbol type, boolean init, StaticO @VmImpl(isJni = true) public @JavaType(Class.class) StaticObject JVM_FindPrimitiveClass(@Pointer TruffleObject namePtr) { Meta meta = getMeta(); - String hostName = NativeUtils.interopPointerToString(namePtr); + String hostName = NativeUtils.interopPointerToStringOrThrow(namePtr, getNativeAccess().nativeMemory(), meta); return findPrimitiveClass(meta, hostName); } @@ -2299,12 +2317,22 @@ private static boolean hasDynamicLoaderCache() { @VmImpl @TruffleBoundary public @Pointer TruffleObject JVM_LoadLibrary(@Pointer TruffleObject namePtr) { - String name = NativeUtils.interopPointerToString(namePtr); // We don't pass `throwException` down due to GR-37925, but even if Sulong would // be fixed, it might be garbage if the used base lib has a mismatching signature, // so we recompute its value instead on our side. boolean throwException = !hasDynamicLoaderCache(); - return JVM_LoadLibrary(name, throwException); + + try { + String name = NativeUtils.interopPointerToString(namePtr, getNativeAccess().nativeMemory()); + return JVM_LoadLibrary(name, throwException); + } catch (IllegalMemoryAccessException e) { + if (throwException) { + throw getContext().getMeta().throwIllegalArgumentExceptionBoundary(e.toString()); + } else { + getLogger().warning("namePtr does not point to a valid memory region: " + e); + return RawPointer.create(0); + } + } } @TruffleBoundary @@ -2374,9 +2402,14 @@ public void JVM_UnloadLibrary(@Pointer TruffleObject libraryPtr) { @TruffleBoundary @SuppressFBWarnings(value = "AT_OPERATION_SEQUENCE_ON_CONCURRENT_ABSTRACTION", justification = "benign race") public @Pointer TruffleObject JVM_FindLibraryEntry(@Pointer TruffleObject libraryPtr, @Pointer TruffleObject namePtr) { - String name = NativeUtils.interopPointerToString(namePtr); - long nativePtr = NativeUtils.interopAsPointer(libraryPtr); - return RawPointer.create(findLibraryEntry(nativePtr, name)); + try { + String name = NativeUtils.interopPointerToString(namePtr, getNativeAccess().nativeMemory()); + long nativePtr = NativeUtils.interopAsPointer(libraryPtr); + return RawPointer.create(findLibraryEntry(nativePtr, name)); + } catch (IllegalMemoryAccessException e) { + getLogger().warning("namePtr does not point to a valid memory region: " + e); + return RawPointer.create(0); + } } @TruffleBoundary @@ -3548,10 +3581,16 @@ public int JNI_GetCreatedJavaVMs(@Pointer TruffleObject vmBufPtr, int bufLen, @P if (getUncached().isNull(vmBufPtr)) { // Pointer should have been pre-null-checked. return JNI_ERR; + } - NativeUtils.writeToPointerPointer(getUncached(), vmBufPtr, getVM().getJavaVM()); - if (!getUncached().isNull(numVMsPtr)) { - NativeUtils.writeToIntPointer(getUncached(), numVMsPtr, 1); + try { + NativeUtils.writeToPointerPointer(getUncached(), vmBufPtr, getVM().getJavaVM(), getNativeAccess().nativeMemory()); + if (!getUncached().isNull(numVMsPtr)) { + NativeUtils.writeToIntPointer(getUncached(), numVMsPtr, 1, getNativeAccess().nativeMemory()); + } + } catch (IllegalMemoryAccessException e) { + getLogger().warning("vmBufPtr or numVMsPtr does not point to a valid memory region: " + e); + return JNI_ERR; } } return JNI_OK; @@ -3654,7 +3693,8 @@ public int JNI_GetCreatedJavaVMs(@Pointer TruffleObject vmBufPtr, int bufLen, @P profiler.profile(0); throw getMeta().throwNullPointerException(); } - ModulesHelperVM.addModuleExports(from_module, NativeUtils.interopPointerToString(pkgName), to_module, getMeta(), profiler); + String pkgNameString = NativeUtils.interopPointerToStringOrThrow(pkgName, getNativeAccess().nativeMemory(), getMeta()); + ModulesHelperVM.addModuleExports(from_module, pkgNameString, to_module, getMeta(), profiler); } @VmImpl(isJni = true) @@ -3665,7 +3705,8 @@ public int JNI_GetCreatedJavaVMs(@Pointer TruffleObject vmBufPtr, int bufLen, @P profiler.profile(0); throw getMeta().throwNullPointerException(); } - ModulesHelperVM.addModuleExportsToAllUnnamed(from_module, NativeUtils.interopPointerToString(pkgName), profiler, getMeta()); + String pkgNameString = NativeUtils.interopPointerToStringOrThrow(pkgName, getNativeAccess().nativeMemory(), getMeta()); + ModulesHelperVM.addModuleExportsToAllUnnamed(from_module, pkgNameString, profiler, getMeta()); } @VmImpl(isJni = true) @@ -3676,7 +3717,8 @@ public int JNI_GetCreatedJavaVMs(@Pointer TruffleObject vmBufPtr, int bufLen, @P profiler.profile(0); throw getMeta().throwNullPointerException(); } - ModulesHelperVM.addModuleExports(from_module, NativeUtils.interopPointerToString(pkgName), StaticObject.NULL, getMeta(), profiler); + String pkgNameString = NativeUtils.interopPointerToStringOrThrow(pkgName, getNativeAccess().nativeMemory(), getMeta()); + ModulesHelperVM.addModuleExports(from_module, pkgNameString, StaticObject.NULL, getMeta(), profiler); } @VmImpl(isJni = true) @@ -3837,10 +3879,10 @@ private String[] extractNativePackages(TruffleObject pkgs, int numPackages, Subs String[] packages = new String[numPackages]; try { for (int i = 0; i < numPackages; i++) { - String pkg = NativeUtils.interopPointerToString((TruffleObject) getUncached().execute(getPackageAt, pkgs, i)); + Meta meta = getMeta(); + String pkg = NativeUtils.interopPointerToStringOrThrow((TruffleObject) getUncached().execute(getPackageAt, pkgs, i), getNativeAccess().nativeMemory(), meta); if (!Validation.validBinaryName(pkg)) { profiler.profile(7); - Meta meta = getMeta(); throw meta.throwExceptionWithMessage(meta.java_lang_IllegalArgumentException, cat("Invalid package name: ", pkg)); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/structs/JavaMemberOffsetGetter.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/structs/JavaMemberOffsetGetter.java index 843e684ebfbe..851963152abb 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/structs/JavaMemberOffsetGetter.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/structs/JavaMemberOffsetGetter.java @@ -22,14 +22,19 @@ */ package com.oracle.truffle.espresso.vm.structs; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.IllegalMemoryAccessException; + import java.util.Collections; import java.util.HashMap; import java.util.Map; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; import com.oracle.truffle.espresso.ffi.nfi.NativeUtils; import com.oracle.truffle.espresso.jni.JNIHandles; +import com.oracle.truffle.espresso.meta.EspressoError; /** * Uses the obtained offsets regarding the structure that stores offset information to build the @@ -41,23 +46,32 @@ public class JavaMemberOffsetGetter implements MemberOffsetGetter { private final Map memberInfos; - public JavaMemberOffsetGetter(JNIHandles handles, TruffleObject memberInfo, Structs structs) { - this.memberInfos = buildInfos(handles, structs, memberInfo); + public JavaMemberOffsetGetter(JNIHandles handles, NativeMemory nativeMemory, TruffleObject memberInfo, Structs structs) { + this.memberInfos = buildInfos(handles, nativeMemory, structs, memberInfo); } - private static Map buildInfos(JNIHandles handles, Structs structs, TruffleObject info) { + private static Map buildInfos(JNIHandles handles, NativeMemory nativeMemory, Structs structs, TruffleObject info) { Map map = new HashMap<>(); InteropLibrary library = InteropLibrary.getUncached(); assert !library.isNull(info); - TruffleObject current = NativeUtils.dereferencePointerPointer(library, info); - while (!library.isNull(current)) { - MemberInfo.MemberInfoWrapper wrapper = structs.memberInfo.wrap(handles, current); - long offset = wrapper.offset(); - String str = NativeUtils.interopPointerToString(wrapper.id()); - map.put(str, offset); - current = wrapper.next(); + try { + TruffleObject current = NativeUtils.dereferencePointerPointer(library, info, nativeMemory); + while (!library.isNull(current)) { + MemberInfo.MemberInfoWrapper wrapper = structs.memberInfo.wrap(handles, nativeMemory, current); + long offset = wrapper.offset(); + String str = NativeUtils.interopPointerToString(wrapper.id(), nativeMemory); + map.put(str, offset); + current = wrapper.next(); + } + return Collections.unmodifiableMap(map); + } catch (IllegalMemoryAccessException e) { + /* + * We should not reach here as we are in control of the arguments and the struct set up + * process. Thus, there should be no illegal memory accesses. + */ + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere(e); } - return Collections.unmodifiableMap(map); } @Override diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/structs/NativeMemberOffsetGetter.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/structs/NativeMemberOffsetGetter.java index 3d6438a4f43e..9ee881484ead 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/structs/NativeMemberOffsetGetter.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/structs/NativeMemberOffsetGetter.java @@ -28,18 +28,24 @@ import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.interop.UnsupportedTypeException; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory.MemoryAllocationException; import com.oracle.truffle.espresso.jni.RawBuffer; import com.oracle.truffle.espresso.meta.EspressoError; +import com.oracle.truffle.espresso.meta.Meta; +import com.oracle.truffle.espresso.runtime.EspressoContext; public final class NativeMemberOffsetGetter implements MemberOffsetGetter { private final InteropLibrary library; private final TruffleObject memberInfoPtr; private final TruffleObject lookupMemberOffset; + private final NativeMemory nativeMemory; - public NativeMemberOffsetGetter(InteropLibrary library, TruffleObject memberInfoPtr, TruffleObject lookupMemberOffset) { + public NativeMemberOffsetGetter(InteropLibrary library, TruffleObject memberInfoPtr, TruffleObject lookupMemberOffset, NativeMemory nativeMemory) { this.library = library; this.memberInfoPtr = memberInfoPtr; this.lookupMemberOffset = lookupMemberOffset; + this.nativeMemory = nativeMemory; } @Override @@ -53,10 +59,19 @@ public long getInfo(String str) { } private long lookupInfo(String str) { - try (RawBuffer memberBuffer = RawBuffer.getNativeString(str)) { + try (RawBuffer memberBuffer = RawBuffer.getNativeString(str, nativeMemory)) { return (long) library.execute(lookupMemberOffset, memberInfoPtr, memberBuffer.pointer()); } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) { throw EspressoError.shouldNotReachHere(e); + } catch (MemoryAllocationException e) { + /* + * This should be very rare as the maximum allocation size would need to be exceeded by + * "str" or we would run out of memory. Thus, the expensive EspressoContext.get + * operation is reasonable. + */ + EspressoContext context = EspressoContext.get(library); + Meta meta = context.getMeta(); + throw meta.throwExceptionWithMessage(meta.java_lang_OutOfMemoryError, e.getMessage(), context); } } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/structs/StructStorage.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/structs/StructStorage.java index 5876d4fd4c97..3acfa6216f13 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/structs/StructStorage.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/structs/StructStorage.java @@ -22,9 +22,15 @@ */ package com.oracle.truffle.espresso.vm.structs; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.MemoryAllocationException; + import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.espresso.ffi.NativeAccess; +import com.oracle.truffle.espresso.ffi.RawPointer; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; import com.oracle.truffle.espresso.jni.JNIHandles; +import com.oracle.truffle.espresso.meta.Meta; +import com.oracle.truffle.espresso.runtime.EspressoContext; /** * Commodity class that stores native structs sizes, along with member offsets. See documentation @@ -37,11 +43,22 @@ public StructStorage(long structSize) { this.structSize = structSize; } - public abstract T wrap(JNIHandles handles, TruffleObject structPtr); + public abstract T wrap(JNIHandles handles, NativeMemory nativeMemory, TruffleObject structPtr); public T allocate(NativeAccess nativeAccess, JNIHandles handles) { - TruffleObject pointer = nativeAccess.allocateMemory(structSize); - return wrap(handles, pointer); + try { + TruffleObject pointer = RawPointer.create(nativeAccess.nativeMemory().allocateMemory(structSize)); + return wrap(handles, nativeAccess.nativeMemory(), pointer); + } catch (MemoryAllocationException e) { + /* + * This should be very rare as the maximum allocation size would need to be exceeded by + * the "structsize", or we would run out of memory. Thus, the expensive + * EspressoContext.get operation is reasonable. + */ + EspressoContext context = EspressoContext.get(null); + Meta meta = context.getMeta(); + throw meta.throwExceptionWithMessage(meta.java_lang_OutOfMemoryError, e.getMessage(), context); + } } public long structSize() { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/structs/StructWrapper.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/structs/StructWrapper.java index a365b73cadc2..1b646afac15e 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/structs/StructWrapper.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/structs/StructWrapper.java @@ -28,14 +28,18 @@ import static com.oracle.truffle.espresso.ffi.NativeType.LONG; import static com.oracle.truffle.espresso.ffi.NativeType.OBJECT; import static com.oracle.truffle.espresso.ffi.NativeType.POINTER; +import static com.oracle.truffle.espresso.ffi.memory.NativeMemory.IllegalMemoryAccessException; import java.nio.ByteBuffer; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.espresso.ffi.NativeAccess; import com.oracle.truffle.espresso.ffi.RawPointer; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; import com.oracle.truffle.espresso.ffi.nfi.NativeUtils; import com.oracle.truffle.espresso.jni.JNIHandles; +import com.oracle.truffle.espresso.meta.EspressoError; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; import com.oracle.truffle.espresso.vm.structs.GenerateStructs.KnownStruct; @@ -50,9 +54,10 @@ * the processor will generate two classes for each of them: *
    *
  • A {@link StructStorage storage class} to store the size of the struct, and the offsets of - * each struct member. It also provides a {@link StructStorage#wrap(JNIHandles, TruffleObject)} - * method, that returns an instance of {@link StructWrapper this class}. These classes are intended - * to be per-context singletons.
  • + * each struct member. It also provides a + * {@link StructStorage#wrap(JNIHandles,NativeMemory, TruffleObject)} method, that returns an + * instance of {@link StructWrapper this class}. These classes are intended to be per-context + * singletons. *
  • A {@link StructWrapper wrapper class}, as described above. This generated class will also * have public getters and setters for each member of the struct.
  • *
@@ -769,13 +774,25 @@ public TruffleObject pointer() { } public void free(NativeAccess nativeAccess) { - nativeAccess.freeMemory(pointer); + try { + nativeAccess.nativeMemory().freeMemory(NativeUtils.interopAsPointer(pointer)); + } catch (IllegalMemoryAccessException e) { + // Should not reach here as we are in control of the arguments! + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere(e); + } } - protected StructWrapper(JNIHandles handles, TruffleObject pointer, long capacity) { + protected StructWrapper(JNIHandles handles, NativeMemory nativeMemory, TruffleObject pointer, long capacity) { this.handles = handles; this.pointer = pointer; - this.buffer = NativeUtils.directByteBuffer(pointer, capacity); + try { + this.buffer = nativeMemory.wrapNativeMemory(NativeUtils.interopAsPointer(pointer), Math.toIntExact(capacity)); + } catch (IllegalMemoryAccessException e) { + // Should not reach here as we are in control of the arguments! + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere(e); + } } protected boolean getBoolean(int offset) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/structs/StructsAccess.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/structs/StructsAccess.java index 764291de04ae..d8119e21c2e0 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/structs/StructsAccess.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/structs/StructsAccess.java @@ -63,7 +63,7 @@ private static Structs initializeStructs(EspressoContext context, TruffleObject @CompilerDirectives.TruffleBoundary public Object call(Object... args) { TruffleObject memberInfoPtr = (TruffleObject) args[0]; - box[0] = new Structs(context.getHandles(), memberInfoPtr, lookupMemberOffset); + box[0] = new Structs(context.getHandles(), context.getNativeAccess().nativeMemory(), memberInfoPtr, lookupMemberOffset); return RawPointer.nullInstance(); } });