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.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.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.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, List 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..8a72c78b006c 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; @@ -210,33 +211,6 @@ default boolean isBuiltIn(@SuppressWarnings("unused") String libname) { return bindSymbol(symbol, nativeSignature); } - /** - * Similar to malloc. The result of allocating a 0-sized buffer is an implementation detail. - * - *

Lifetime - * - * @return null if the memory cannot be allocated. Otherwise, a - * {@link InteropLibrary#hasBufferElements(Object) buffer}. - * @throws IllegalArgumentException if the size is negative - */ - @Buffer - TruffleObject allocateMemory(long size); - - /** - * 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); - - /** - * Similar to free. Accessing the buffer after free may cause explosive undefined behavior. - */ - void freeMemory(@Pointer TruffleObject buffer); - /** * Sinking, make a Java method accessible to the native world. Returns an * {@link InteropLibrary#isPointer(Object) pointer} {@link TruffleObject object}, callable from @@ -292,4 +266,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/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/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; + } } 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..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,10 +22,9 @@ */ package com.oracle.truffle.espresso.ffi.nfi; -import static com.oracle.truffle.espresso.substitutions.standard.Target_sun_misc_Unsafe.ADDRESS_SIZE; +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; @@ -38,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 @@ -102,112 +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()); - } - - @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/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/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"); } } 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(); } });