From a26d37aa8b375521ad38db4b6a827328afda6a44 Mon Sep 17 00:00:00 2001 From: Elia Trachsel Date: Mon, 11 Aug 2025 15:24:07 +0200 Subject: [PATCH 01/26] Lays the foundation for the incoming EspressoLibs substitutions. This includes: - Adding projects io.jdk21 and io.jdk25 to cope with internal API changes - Adding the apporpriate ExtraJavaHomes to CI - Adding a jdk21 daily job to the CI for testing EspressoLibs on 21 - Introduces LibsMeta to conditionally load meta-data used in EspressoLibs --- ci/common.jsonnet | 8 ++ common.json | 2 +- .../espresso/classfile/JavaVersion.java | 4 + espresso/ci/ci_common/common.jsonnet | 2 +- espresso/mx.espresso/suite.py | 36 ++++++++- .../src/java/io/TruffleFileSystem.java | 6 +- .../src/sun/nio/ch/FileDispatcherImpl.java | 3 +- .../sun/nio/fs/DefaultFileSystemProvider.java | 3 +- .../src/sun/nio/fs/MapFilterIterator.java | 3 +- .../nio/fs/TruffleBasicFileAttributeView.java | 6 +- .../nio/fs/TruffleBasicFileAttributes.java | 6 +- .../src/sun/nio/fs/TruffleFileSystem.java | 3 +- .../sun/nio/fs/TruffleFileSystemProvider.java | 3 +- .../fs/TruffleFilteredDirectoryStream.java | 3 +- .../src/sun/nio/fs/TrufflePath.java | 3 +- .../espresso/descriptors/EspressoSymbols.java | 5 -- .../ffi/EspressoLibsNativeAccess.java | 6 +- .../truffle/espresso/libs/LibsMeta.java | 79 +++++++++++++++++++ .../truffle/espresso/libs/LibsState.java | 12 ++- .../libjava/impl/Target_java_lang_System.java | 22 +++++- .../impl/Target_java_util_zip_CRC32.java | 34 ++++---- .../impl/Target_java_util_zip_Inflater.java | 74 +++++------------ .../espresso/meta/DiffVersionLoadHelper.java | 24 +++--- .../oracle/truffle/espresso/meta/Meta.java | 43 ---------- .../truffle/espresso/runtime/Classpath.java | 5 +- .../espresso/runtime/EspressoContext.java | 11 +++ vm/ci/ci_common/common-runspec.jsonnet | 2 +- vm/ci/ci_common/common.jsonnet | 2 +- 28 files changed, 244 insertions(+), 166 deletions(-) create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsMeta.java diff --git a/ci/common.jsonnet b/ci/common.jsonnet index 4272ca2d1516..1b553d098b01 100644 --- a/ci/common.jsonnet +++ b/ci/common.jsonnet @@ -239,6 +239,14 @@ local common_json = import "../common.json"; } else {}, }, + espresso:: { + downloads+: { + EXTRA_JAVA_HOMES+: { + pathlist+: [jdks_data["oraclejdk21"], jdks_data["oraclejdk25"]], + } + } + }, + sulong:: self.cmake + { packages+: if self.os == "windows" then { msvc_source: "==14.0", diff --git a/common.json b/common.json index fea05a770214..efc4ce54e798 100644 --- a/common.json +++ b/common.json @@ -4,7 +4,7 @@ "Jsonnet files should not include this file directly but use ci/common.jsonnet instead." ], - "mx_version": "7.58.9", + "mx_version": "7.59.0", "COMMENT.jdks": "When adding or removing JDKs keep in sync with JDKs in ci/common.jsonnet", "jdks": { diff --git a/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/JavaVersion.java b/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/JavaVersion.java index 0e30149ecea2..2722980ad66b 100644 --- a/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/JavaVersion.java +++ b/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/JavaVersion.java @@ -111,6 +111,10 @@ public static JavaVersion forVersion(String version) { } } + public Runtime.Version toRunTimeVersion() { + return Runtime.Version.parse(toString()); + } + private static JavaVersion forVersion(Runtime.Version version) { return forVersion(version.feature()); } diff --git a/espresso/ci/ci_common/common.jsonnet b/espresso/ci/ci_common/common.jsonnet index 5e7387231ba6..c9a11eb234aa 100644 --- a/espresso/ci/ci_common/common.jsonnet +++ b/espresso/ci/ci_common/common.jsonnet @@ -15,7 +15,7 @@ local benchmark_suites = ['dacapo', 'renaissance', 'scala-dacapo']; local that = self, // platform-specific snippets - common: graal_common.deps.sulong + { + common: graal_common.deps.sulong + graal_common.deps.espresso + { python_version: '3', environment+: { GRAALVM_CHECK_EXPERIMENTAL_OPTIONS: "true", diff --git a/espresso/mx.espresso/suite.py b/espresso/mx.espresso/suite.py index d03ecb97d92c..613ee6fd7a43 100644 --- a/espresso/mx.espresso/suite.py +++ b/espresso/mx.espresso/suite.py @@ -101,7 +101,41 @@ "sourceDirs": ["src"], # Contains classes in sun.nio.* that only compile with javac. "forceJavac": "true", - "javaCompliance": "8+", + "javaCompliance": "21+", + "patchModule": "java.base", + "checkPackagePrefix": False, # Contains classes in java.io and sun.nio. + "checkstyle": "com.oracle.truffle.espresso", + }, + + "com.oracle.truffle.espresso.io.jdk21": { + "subDir": "src", + "sourceDirs": ["src"], + "dependencies": [ + "com.oracle.truffle.espresso.io", + ], + # Contains classes in sun.nio.* that only compile with javac. + "overlayTarget": "com.oracle.truffle.espresso.io", + "forceJavac": "true", + "multiReleaseJarVersion": "21", + "patchModule": "java.base", + "javaCompliance": "21", + "checkPackagePrefix": False, # Contains classes in java.io and sun.nio. + "checkstyle": "com.oracle.truffle.espresso", + }, + + "com.oracle.truffle.espresso.io.jdk25": { + "subDir": "src", + "sourceDirs": ["src"], + "dependencies": [ + "com.oracle.truffle.espresso.io", + ], + # Contains classes in sun.nio.* that only compile with javac. + "spotbugs": "false", # inorder to pass the gate + "overlayTarget": "com.oracle.truffle.espresso.io", + "forceJavac": "true", + "multiReleaseJarVersion": "25", + "patchModule": "java.base", + "javaCompliance": "25", "checkPackagePrefix": False, # Contains classes in java.io and sun.nio. "checkstyle": "com.oracle.truffle.espresso", }, diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/java/io/TruffleFileSystem.java b/espresso/src/com.oracle.truffle.espresso.io/src/java/io/TruffleFileSystem.java index 15c20c810abf..bfc60c28c6f5 100644 --- a/espresso/src/com.oracle.truffle.espresso.io/src/java/io/TruffleFileSystem.java +++ b/espresso/src/com.oracle.truffle.espresso.io/src/java/io/TruffleFileSystem.java @@ -27,9 +27,7 @@ *

* Its native methods are provided by Espresso's custom {@code libjava} implementation. *

- * This file must be compatible with all Java versions supported by Espresso, strict Java 8 - * compatibility is required. - * + * This file must be compatible with 21+. */ final class TruffleFileSystem extends FileSystem { @@ -166,7 +164,7 @@ public long getSpace(File f, int t) { return getSpace0(f, t); } - @Override + // @Override is defined in 11+ public int getNameMax(String path) { return getNameMax0(path); } diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/FileDispatcherImpl.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/FileDispatcherImpl.java index 36aa9f507c06..2e421bfa56a0 100644 --- a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/FileDispatcherImpl.java +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/FileDispatcherImpl.java @@ -34,8 +34,7 @@ * descriptors used this class are purely virtual and should not be passed down to native code. * *

- * This file must be compatible with all Java versions supported by Espresso, strict Java 8 - * compatibility is required. + * This file must be compatible with 21+ */ final class FileDispatcherImpl extends sun.nio.ch.FileDispatcher { diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/DefaultFileSystemProvider.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/DefaultFileSystemProvider.java index d763f7a49bcf..5fb6ec957166 100644 --- a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/DefaultFileSystemProvider.java +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/DefaultFileSystemProvider.java @@ -28,8 +28,7 @@ * Replaces JDK's own {@link DefaultFileSystemProvider} to link to a Truffle-based * {@code FileSystem}. *

- * This file must be compatible with all Java versions supported by Espresso, strict Java 8 - * compatibility is required. + * This file must be compatible with 21+. */ public final class DefaultFileSystemProvider { private static final TruffleFileSystemProvider INSTANCE = new TruffleFileSystemProvider(); diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/MapFilterIterator.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/MapFilterIterator.java index baf66ec50217..d15cadcd451d 100644 --- a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/MapFilterIterator.java +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/MapFilterIterator.java @@ -32,8 +32,7 @@ * Small utility to apply map + filter operations, in that order, to a base iterator. * *

- * This file must be compatible with all Java versions supported by Espresso, strict Java 8 - * compatibility is required. + * This file must be compatible with 21+. */ final class MapFilterIterator implements Iterator { diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleBasicFileAttributeView.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleBasicFileAttributeView.java index a1c5c7fd82d2..426cb746a19a 100644 --- a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleBasicFileAttributeView.java +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleBasicFileAttributeView.java @@ -26,14 +26,14 @@ import java.nio.file.attribute.BasicFileAttributeView; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; /** - * This file must be compatible with all Java versions supported by Espresso, strict Java 8 - * compatibility is required. + * This file must be compatible with 21+. */ final class TruffleBasicFileAttributeView implements BasicFileAttributeView { @@ -71,7 +71,7 @@ Map readAttributes(String attributes) throws IOException { TruffleBasicFileAttributes bfa = (TruffleBasicFileAttributes) readAttributes(); List queriedAttributes = "*".equals(attributes) ? TruffleBasicFileAttributes.BASIC_ATTRIBUTES - : List.of(attributes.split(",")); + : Arrays.asList(attributes.split(",")); HashMap map = new HashMap<>(); for (String attributeName : queriedAttributes) { diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleBasicFileAttributes.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleBasicFileAttributes.java index 94b8ab4737a2..98a59bd1e1cf 100644 --- a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleBasicFileAttributes.java +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleBasicFileAttributes.java @@ -24,6 +24,7 @@ import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; +import java.util.Arrays; import java.util.List; /** @@ -31,12 +32,11 @@ * attributes are serialized as a lon (milliseconds from the epoch). * *

- * This file must be compatible with all Java versions supported by Espresso, strict Java 8 - * compatibility is required. + * This file must be compatible with 21+. */ final class TruffleBasicFileAttributes implements BasicFileAttributes { - static final List BASIC_ATTRIBUTES = List.of( + static final List BASIC_ATTRIBUTES = Arrays.asList( "lastModifiedTime", "lastAccessTime", "creationTime", diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFileSystem.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFileSystem.java index 9de76f11bf89..0a7361277922 100644 --- a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFileSystem.java +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFileSystem.java @@ -53,8 +53,7 @@ * native code. * *

- * This file must be compatible with all Java versions supported by Espresso, strict Java 8 - * compatibility is required. + * This file must be compatible with 21+. */ final class TruffleFileSystem extends FileSystem { private static final Set SUPPORTED_ATTRIBUTES = Collections.singleton("basic"); diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFileSystemProvider.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFileSystemProvider.java index 1ce296576c97..57fc76815632 100644 --- a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFileSystemProvider.java +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFileSystemProvider.java @@ -51,8 +51,7 @@ import java.util.Set; /** - * This file must be compatible with all Java versions supported by Espresso, strict Java 8 - * compatibility is required. + * This file must be compatible with 21+. */ class TruffleFileSystemProvider extends FileSystemProvider { diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFilteredDirectoryStream.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFilteredDirectoryStream.java index ebb46d071693..eda8750cbdb4 100644 --- a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFilteredDirectoryStream.java +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFilteredDirectoryStream.java @@ -31,8 +31,7 @@ import java.util.Objects; /** - * This file must be compatible with all Java versions supported by Espresso, strict Java 8 - * compatibility is required. + * This file must be compatible with 21+. */ final class TruffleFilteredDirectoryStream implements DirectoryStream { private final TrufflePath truffleDir; diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TrufflePath.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TrufflePath.java index 6db963b8119e..7e408d01c4ea 100644 --- a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TrufflePath.java +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TrufflePath.java @@ -43,8 +43,7 @@ * Truffle VFS implementation of {@link java.nio.file.Path}. * *

- * This file must be compatible with all Java versions supported by Espresso, strict Java 8 - * compatibility is required. + * This file must be compatible with 21+. */ final class TrufflePath implements Path { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java index c6e59f2f6cfc..959223e9fd29 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java @@ -760,11 +760,6 @@ public static class Names { public static final Symbol HIDDEN_CRC32 = SYMBOLS.putName("0HIDDEN_CRC32"); public static final Symbol inputConsumed = SYMBOLS.putName("inputConsumed"); public static final Symbol outputConsumed = SYMBOLS.putName("outputConsumed"); - public static final Symbol len = SYMBOLS.putName("len"); - public static final Symbol off = SYMBOLS.putName("off"); - public static final Symbol needDict = SYMBOLS.putName("needDict"); - public static final Symbol finished = SYMBOLS.putName("finished"); - public static final Symbol buf = SYMBOLS.putName("buf"); // java.lang.invoke.* // CallSite public static final Symbol target = SYMBOLS.putName("target"); 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 118c8a094124..66e7451cd329 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 @@ -67,8 +67,9 @@ public EspressoLibsNativeAccess(EspressoContext ctx, NativeAccess delegate) { @Override public @Pointer TruffleObject loadLibrary(Path libraryPath) { Path libname = libraryPath.getFileName(); - if (libs.isKnown(libraryPath.toString())) { + if (libname != null && libs.isKnown(libraryPath.toString())) { getLogger().fine(() -> "Loading espresso lib: " + libname); + // this return libs.loadLibrary(getContext(), libname.toString()); } @@ -140,6 +141,9 @@ public boolean hasFallbackSymbols() { @Override public boolean isFallbackSymbol(TruffleObject symbol) { + if (symbol instanceof SubstitutionFactoryWrapper) { + return false; + } return delegate.isFallbackSymbol(symbol); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsMeta.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsMeta.java new file mode 100644 index 000000000000..fcd4a3975530 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsMeta.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018, 2025, 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.libs; + +import static com.oracle.truffle.espresso.classfile.JavaVersion.VersionRange.ALL; + +import com.oracle.truffle.espresso.classfile.descriptors.Symbol; +import com.oracle.truffle.espresso.classfile.descriptors.Type; +import com.oracle.truffle.espresso.descriptors.EspressoSymbols; +import com.oracle.truffle.espresso.impl.ContextAccess; +import com.oracle.truffle.espresso.impl.Field; +import com.oracle.truffle.espresso.impl.ObjectKlass; +import com.oracle.truffle.espresso.meta.DiffVersionLoadHelper; +import com.oracle.truffle.espresso.meta.Meta; +import com.oracle.truffle.espresso.runtime.EspressoContext; + +public final class LibsMeta implements ContextAccess { + private final EspressoContext context; + private final Meta meta; + // Checkstyle: stop field name check + // libzip + public final ObjectKlass java_util_zip_CRC32; + public final Field HIDDEN_CRC32; + public final ObjectKlass java_util_zip_Inflater; + public final Field java_util_zip_Inflater_inputConsumed; + public final Field java_util_zip_Inflater_outputConsumed; + public final ObjectKlass java_util_zip_DataFormatException; + // Checkstyle: resume field name check + + @Override + public EspressoContext getContext() { + return context; + } + + public Meta getMeta() { + return meta; + } + + public LibsMeta(EspressoContext ctx) { + this.context = ctx; + this.meta = context.getMeta(); + + // libzip + java_util_zip_CRC32 = knownKlass(EspressoSymbols.Types.java_util_zip_CRC32); + HIDDEN_CRC32 = diff().field(ALL, EspressoSymbols.Names.HIDDEN_CRC32, EspressoSymbols.Types._int).maybeHiddenfield(java_util_zip_CRC32); + java_util_zip_Inflater = knownKlass(EspressoSymbols.Types.java_util_zip_Inflater); + java_util_zip_DataFormatException = knownKlass(EspressoSymbols.Types.java_util_zip_DataFormatException); + java_util_zip_Inflater_inputConsumed = java_util_zip_Inflater.requireDeclaredField(EspressoSymbols.Names.inputConsumed, EspressoSymbols.Types._int); + java_util_zip_Inflater_outputConsumed = java_util_zip_Inflater.requireDeclaredField(EspressoSymbols.Names.outputConsumed, EspressoSymbols.Types._int); + } + + public ObjectKlass knownKlass(Symbol type) { + return meta.knownKlass(type); + } + + private DiffVersionLoadHelper diff() { + return new DiffVersionLoadHelper(meta); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsState.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsState.java index 98f486ec9e3f..ae550079280e 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsState.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsState.java @@ -24,13 +24,17 @@ import java.util.zip.Inflater; -import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.TruffleLogger; +import com.oracle.truffle.espresso.EspressoLanguage; import com.oracle.truffle.espresso.jni.StrongHandles; import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.EspressoException; public class LibsState { + private static final TruffleLogger logger = TruffleLogger.getLogger(EspressoLanguage.ID, LibsState.class); + private final StrongHandles handle2Inflater = new StrongHandles<>(); public long handlifyInflater(Inflater i) { @@ -49,10 +53,14 @@ public Inflater getInflater(long handle) { return inflater; } - @CompilerDirectives.TruffleBoundary + @TruffleBoundary private static EspressoException throwInternalError() { Meta meta = EspressoContext.get(null).getMeta(); return meta.throwExceptionWithMessage(meta.java_lang_InternalError, "the provided handle doesn't correspond to an Inflater"); } + public TruffleLogger getLogger() { + return logger; + } + } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_System.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_System.java index 39ea2333f624..f8b2cdaba667 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_System.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_System.java @@ -86,8 +86,12 @@ public static final class Raw { known[props.userNameNdx] = java.lang.System.getProperty("user.name"); known[props.sunJnuEncodingNdx] = java.lang.System.getProperty("sun.jnu.encoding"); - known[props.fileEncodingNdx] = java.lang.System.getProperty("file.encoding"); - + if (ctx.getJavaVersion().java21OrEarlier()) { + known[props.fileEncodingNdx] = java.lang.System.getProperty("file.encoding"); + } + if (ctx.getJavaVersion().java25OrLater()) { + known[props.nativeEncodingNDX] = java.lang.System.getProperty("native.encoding"); + } known[props.stdoutEncodingNdx] = java.lang.System.getProperty("stdout.encoding"); known[props.stderrEncodingNdx] = java.lang.System.getProperty("stderr.encoding"); @@ -143,6 +147,7 @@ private static final class Props { private final int displayLanguageNdx; private final int displayScriptNdx; private final int displayVariantNdx; + // only in 21- private final int fileEncodingNdx; private final int fileSeparatorNdx; private final int formatCountryNdx; @@ -163,6 +168,8 @@ private static final class Props { private final int osNameNdx; private final int osVersionNdx; private final int pathSeparatorNdx; + // only in 25+ + private final int nativeEncodingNDX; private final int socksNonProxyHostsNdx; private final int socksProxyHostNdx; private final int socksProxyPortNdx; @@ -186,7 +193,16 @@ private Props(EspressoContext ctx) { displayLanguageNdx = guestRaw.lookupDeclaredField(ctx.getNames().getOrCreate("_display_language_NDX"), Types._int).getInt(guestRaw.tryInitializeAndGetStatics()); displayScriptNdx = guestRaw.lookupDeclaredField(ctx.getNames().getOrCreate("_display_script_NDX"), Types._int).getInt(guestRaw.tryInitializeAndGetStatics()); displayVariantNdx = guestRaw.lookupDeclaredField(ctx.getNames().getOrCreate("_display_variant_NDX"), Types._int).getInt(guestRaw.tryInitializeAndGetStatics()); - fileEncodingNdx = guestRaw.lookupDeclaredField(ctx.getNames().getOrCreate("_file_encoding_NDX"), Types._int).getInt(guestRaw.tryInitializeAndGetStatics()); + if (ctx.getJavaVersion().java21OrEarlier()) { + fileEncodingNdx = guestRaw.lookupDeclaredField(ctx.getNames().getOrCreate("_file_encoding_NDX"), Types._int).getInt(guestRaw.tryInitializeAndGetStatics()); + } else { + fileEncodingNdx = -1; + } + if (ctx.getJavaVersion().java25OrLater()) { + nativeEncodingNDX = guestRaw.lookupDeclaredField(ctx.getNames().getOrCreate("_native_encoding_NDX"), Types._int).getInt(guestRaw.tryInitializeAndGetStatics()); + } else { + nativeEncodingNDX = -1; + } fileSeparatorNdx = guestRaw.lookupDeclaredField(ctx.getNames().getOrCreate("_file_separator_NDX"), Types._int).getInt(guestRaw.tryInitializeAndGetStatics()); formatCountryNdx = guestRaw.lookupDeclaredField(ctx.getNames().getOrCreate("_format_country_NDX"), Types._int).getInt(guestRaw.tryInitializeAndGetStatics()); formatLanguageNdx = guestRaw.lookupDeclaredField(ctx.getNames().getOrCreate("_format_language_NDX"), Types._int).getInt(guestRaw.tryInitializeAndGetStatics()); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libzip/impl/Target_java_util_zip_CRC32.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libzip/impl/Target_java_util_zip_CRC32.java index 17510dd954d8..9859971a5dd7 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libzip/impl/Target_java_util_zip_CRC32.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libzip/impl/Target_java_util_zip_CRC32.java @@ -25,8 +25,8 @@ import java.util.zip.CRC32; import com.oracle.truffle.espresso.EspressoLanguage; +import com.oracle.truffle.espresso.libs.LibsMeta; import com.oracle.truffle.espresso.libs.libzip.LibZip; -import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; import com.oracle.truffle.espresso.substitutions.Inject; @@ -35,54 +35,54 @@ @EspressoSubstitutions(value = CRC32.class, group = LibZip.class) public final class Target_java_util_zip_CRC32 { - private static CRC32 getHostCRC32(StaticObject crc, Meta meta) { + private static CRC32 getHostCRC32(StaticObject crc, LibsMeta lMeta) { if (StaticObject.isNull(crc)) { - throw meta.throwNullPointerException(); + throw lMeta.getMeta().throwNullPointerException(); } - Object hostCRC = meta.HIDDEN_CRC32.getHiddenObject(crc); + Object hostCRC = lMeta.HIDDEN_CRC32.getHiddenObject(crc); assert hostCRC != null; return (CRC32) hostCRC; } @Substitution public static void init(@JavaType(CRC32.class) StaticObject crc, - @Inject Meta meta) { - meta.HIDDEN_CRC32.setHiddenObject(crc, new CRC32()); + @Inject LibsMeta lMeta) { + lMeta.HIDDEN_CRC32.setHiddenObject(crc, new CRC32()); } @Substitution public static void update0(@JavaType(CRC32.class) StaticObject crc, int b, - @Inject Meta meta) { + @Inject LibsMeta lMeta) { try { - getHostCRC32(crc, meta).update(b); + getHostCRC32(crc, lMeta).update(b); } catch (IndexOutOfBoundsException e) { - meta.throwExceptionWithMessage(meta.java_lang_IndexOutOfBoundsException, e.getMessage()); + lMeta.getMeta().throwExceptionWithMessage(lMeta.getMeta().java_lang_IndexOutOfBoundsException, e.getMessage()); } } @Substitution public static void updateBytes0(@JavaType(CRC32.class) StaticObject crc, @JavaType(byte[].class) StaticObject b, int off, int len, - @Inject Meta meta, @Inject EspressoLanguage lang) { + @Inject LibsMeta lMeta, @Inject EspressoLanguage lang) { if (StaticObject.isNull(b)) { - throw meta.throwNullPointerException(); + throw lMeta.getMeta().throwNullPointerException(); } assert b.isArray(); try { - getHostCRC32(crc, meta).update(b.unwrap(lang), off, len); + getHostCRC32(crc, lMeta).update(b.unwrap(lang), off, len); } catch (IndexOutOfBoundsException e) { - meta.throwExceptionWithMessage(meta.java_lang_IndexOutOfBoundsException, e.getMessage()); + lMeta.getMeta().throwExceptionWithMessage(lMeta.getMeta().java_lang_IndexOutOfBoundsException, e.getMessage()); } } @Substitution public static long getValue0(@JavaType(CRC32.class) StaticObject crc, - @Inject Meta meta) { - return getHostCRC32(crc, meta).getValue(); + @Inject LibsMeta lMeta) { + return getHostCRC32(crc, lMeta).getValue(); } @Substitution public static void reset0(@JavaType(CRC32.class) StaticObject crc, - @Inject Meta meta) { - getHostCRC32(crc, meta).reset(); + @Inject LibsMeta lMeta) { + getHostCRC32(crc, lMeta).reset(); } } 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 ab5275c5d9e2..9b396b48ab3e 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 @@ -29,15 +29,14 @@ 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.staticobject.StaticObject; import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; import com.oracle.truffle.espresso.substitutions.Inject; import com.oracle.truffle.espresso.substitutions.JavaType; import com.oracle.truffle.espresso.substitutions.Substitution; -import com.oracle.truffle.espresso.substitutions.VersionFilter; /** * EspressoLibs substitution for the Inflater class. The implementation associates a HostInflater @@ -73,18 +72,18 @@ public static void setDictionary(long addr, @JavaType(byte[].class) StaticObject libsState.getInflater(addr).setDictionary(byteArrayB, off, len); } - @Substitution(languageFilter = VersionFilter.Java11OrLater.class) + @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); } - @Substitution(hasReceiver = true, languageFilter = VersionFilter.Java11OrLater.class) + @Substitution(hasReceiver = true) @TruffleBoundary public static long inflateBytesBytes(@JavaType(Inflater.class) StaticObject guestInflater, long addr, @JavaType(byte[].class) StaticObject inputArray, int inputOff, int inputLen, @JavaType(byte[].class) StaticObject outputArray, int outputOff, int outputLen, - @Inject LibsState libsState, @Inject Meta meta, @Inject EspressoLanguage language) { + @Inject LibsState libsState, @Inject LibsMeta libsMeta, @Inject EspressoLanguage language) { // get Input/Output Array/Buffer byte[] inputByteArray = inputArray.unwrap(language); byte[] outputByteArray = outputArray.unwrap(language); @@ -99,16 +98,15 @@ public static long inflateBytesBytes(@JavaType(Inflater.class) StaticObject gues int read = Math.toIntExact(hostInflater.getBytesRead() - bytesReadOld); return encodeResult(read, written, hostInflater); } catch (DataFormatException e) { - updateGuestInflater(hostInflater, bytesReadOld, bytesWrittenOld, guestInflater, meta); - throw meta.throwExceptionWithMessage(meta.java_util_zip_DataFormatException, e.getMessage()); + updateGuestInflater(hostInflater, bytesReadOld, bytesWrittenOld, guestInflater, libsMeta); + throw libsMeta.getMeta().throwExceptionWithMessage(libsMeta.java_util_zip_DataFormatException, e.getMessage()); } } - @Substitution(hasReceiver = true, languageFilter = VersionFilter.Java11OrLater.class) + @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 Meta meta, @Inject EspressoLanguage lang) { + long outputAddress, int outputLen, @Inject LibsState libsState, @Inject LibsMeta libsMeta, @Inject EspressoLanguage lang) { // get Input/Output Array/Buffer byte[] inputByteArray = inputArray.unwrap(lang); ByteBuffer outputByteBuffer = NativeUtils.directByteBuffer(outputAddress, outputLen); @@ -123,16 +121,16 @@ public static long inflateBytesBuffer(@JavaType(Inflater.class) StaticObject gue int read = Math.toIntExact(hostInflater.getBytesRead() - bytesReadOld); return encodeResult(read, written, hostInflater); } catch (DataFormatException e) { - updateGuestInflater(hostInflater, bytesReadOld, bytesWrittenOld, guestInflater, meta); - throw meta.throwExceptionWithMessage(meta.java_util_zip_DataFormatException, e.getMessage()); + updateGuestInflater(hostInflater, bytesReadOld, bytesWrittenOld, guestInflater, libsMeta); + throw libsMeta.getMeta().throwExceptionWithMessage(libsMeta.java_util_zip_DataFormatException, e.getMessage()); } } - @Substitution(hasReceiver = true, languageFilter = VersionFilter.Java11OrLater.class) + @Substitution(hasReceiver = true) 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 Meta meta, + @Inject LibsState libsState, @Inject LibsMeta libsMeta, @Inject EspressoLanguage lang) { // get Input/Output Array/Buffer @@ -149,17 +147,17 @@ public static long inflateBufferBytes(@JavaType(Inflater.class) StaticObject gue int read = Math.toIntExact(hostInflater.getBytesRead() - bytesReadOld); return encodeResult(read, written, hostInflater); } catch (DataFormatException e) { - updateGuestInflater(hostInflater, bytesReadOld, bytesWrittenOld, guestInflater, meta); - throw meta.throwExceptionWithMessage(meta.java_util_zip_DataFormatException, e.getMessage()); + updateGuestInflater(hostInflater, bytesReadOld, bytesWrittenOld, guestInflater, libsMeta); + throw libsMeta.getMeta().throwExceptionWithMessage(libsMeta.java_util_zip_DataFormatException, e.getMessage()); } } - @Substitution(hasReceiver = true, languageFilter = VersionFilter.Java11OrLater.class) + @Substitution(hasReceiver = true) @SuppressWarnings("unused") public static long inflateBufferBuffer(@JavaType(Inflater.class) StaticObject guestInflater, long addr, long inputAddress, int inputLen, long outputAddress, int outputLen, - @Inject LibsState libsState, @Inject Meta meta, + @Inject LibsState libsState, @Inject LibsMeta libsMeta, @Inject EspressoLanguage lang) { // get Input/Output Array/Buffer ByteBuffer inputByteBuffer = NativeUtils.directByteBuffer(inputAddress, inputLen); @@ -175,47 +173,17 @@ public static long inflateBufferBuffer(@JavaType(Inflater.class) StaticObject gu int read = Math.toIntExact(hostInflater.getBytesRead() - bytesReadOld); return encodeResult(read, written, hostInflater); } catch (DataFormatException e) { - updateGuestInflater(hostInflater, bytesReadOld, bytesWrittenOld, guestInflater, meta); - throw meta.throwExceptionWithMessage(meta.java_util_zip_DataFormatException, e.getMessage()); - } - } - - @Substitution(hasReceiver = true, languageFilter = VersionFilter.Java8OrEarlier.class) - public static int inflateBytes(@JavaType(Inflater.class) StaticObject guestInflater, long addr, - @JavaType(byte[].class) StaticObject outputArray, - int outputOff, int outputLen, - @Inject LibsState libsState, @Inject Meta meta, - @Inject EspressoLanguage lang) { - // get Input/Output Array/Buffer - byte[] inputArr = meta.java_util_zip_Inflater_buf.getObject(guestInflater).unwrap(lang); - int off = meta.java_util_zip_Inflater_off.getInt(guestInflater); - int len = meta.java_util_zip_Inflater_len.getInt(guestInflater); - byte[] outputByteArray = outputArray.unwrap(lang); - // get host Inflater and set Input - Inflater hostInflater = libsState.getInflater(addr); - hostInflater.setInput(inputArr, off, len); - // cache bytesReadOld - long bytesReadOld = hostInflater.getBytesRead(); - try { - int written = hostInflater.inflate(outputByteArray, outputOff, outputLen); - // update fields - int read = Math.toIntExact(hostInflater.getBytesRead() - bytesReadOld); - meta.java_util_zip_Inflater_off.setInt(guestInflater, off + read); - meta.java_util_zip_Inflater_len.setInt(guestInflater, hostInflater.getRemaining()); - meta.java_util_zip_Inflater_needDict.setBoolean(guestInflater, hostInflater.needsDictionary()); - meta.java_util_zip_Inflater_finished.setBoolean(guestInflater, hostInflater.finished()); - return written; - } catch (DataFormatException e) { - throw meta.throwExceptionWithMessage(meta.java_util_zip_DataFormatException, e.getMessage()); + updateGuestInflater(hostInflater, bytesReadOld, bytesWrittenOld, guestInflater, libsMeta); + throw libsMeta.getMeta().throwExceptionWithMessage(libsMeta.java_util_zip_DataFormatException, e.getMessage()); } } private static void updateGuestInflater(Inflater hostInflater, long bytesReadOld, long bytesWrittenOld, - @JavaType(Inflater.class) StaticObject guestInflater, Meta meta) { + @JavaType(Inflater.class) StaticObject guestInflater, LibsMeta libsMeta) { int inputConsumed = Math.toIntExact(hostInflater.getBytesRead() - bytesReadOld); int outputConsumed = Math.toIntExact(hostInflater.getBytesWritten() - bytesWrittenOld); - meta.java_util_zip_Inflater_inputConsumed.setInt(guestInflater, inputConsumed); - meta.java_util_zip_Inflater_outputConsumed.setInt(guestInflater, outputConsumed); + libsMeta.java_util_zip_Inflater_inputConsumed.setInt(guestInflater, inputConsumed); + libsMeta.java_util_zip_Inflater_outputConsumed.setInt(guestInflater, outputConsumed); } @Substitution diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/DiffVersionLoadHelper.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/DiffVersionLoadHelper.java index 281638c03b57..dcbad253996c 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/DiffVersionLoadHelper.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/DiffVersionLoadHelper.java @@ -31,39 +31,39 @@ import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.impl.ObjectKlass; -final class DiffVersionLoadHelper { +public final class DiffVersionLoadHelper { private final Meta meta; private Symbol name; private Symbol type; private Symbol signature; - DiffVersionLoadHelper(Meta meta) { + public DiffVersionLoadHelper(Meta meta) { this.meta = meta; } - DiffVersionLoadHelper klass(VersionRange range, Symbol t) { + public DiffVersionLoadHelper klass(VersionRange range, Symbol t) { if (range.contains(meta.getJavaVersion())) { this.type = t; } return this; } - ObjectKlass klass() { + public ObjectKlass klass() { if (type == null) { throw EspressoError.shouldNotReachHere(); } return meta.knownKlass(type); } - ObjectKlass notRequiredKlass() { + public ObjectKlass notRequiredKlass() { if (type == null) { return null; } return meta.loadKlassWithBootClassLoader(type); } - DiffVersionLoadHelper method(VersionRange range, Symbol n, Symbol s) { + public DiffVersionLoadHelper method(VersionRange range, Symbol n, Symbol s) { if (range.contains(meta.getJavaVersion())) { this.name = n; this.signature = s; @@ -71,14 +71,14 @@ DiffVersionLoadHelper method(VersionRange range, Symbol n, Symbol n, Symbol t) { + public DiffVersionLoadHelper field(VersionRange range, Symbol n, Symbol t) { if (range.contains(meta.getJavaVersion())) { this.name = n; this.type = t; @@ -96,14 +96,14 @@ DiffVersionLoadHelper field(VersionRange range, Symbol n, Symbol t) return this; } - Field field(ObjectKlass klass) { + public Field field(ObjectKlass klass) { if (name == null || type == null) { throw EspressoError.shouldNotReachHere(); } return klass.requireDeclaredField(name, type); } - Field notRequiredField(ObjectKlass klass) { + public Field notRequiredField(ObjectKlass klass) { if (name == null || type == null) { return null; } @@ -113,7 +113,7 @@ Field notRequiredField(ObjectKlass klass) { return klass.requireDeclaredField(name, type); } - Field maybeHiddenfield(ObjectKlass klass) { + public Field maybeHiddenfield(ObjectKlass klass) { if (name == null || type == null) { return null; } 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 084265b4dd22..857107fc9384 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 @@ -23,7 +23,6 @@ package com.oracle.truffle.espresso.meta; import static com.oracle.truffle.espresso.EspressoOptions.SpecComplianceMode.HOTSPOT; -import static com.oracle.truffle.espresso.classfile.JavaVersion.VersionRange.ALL; import static com.oracle.truffle.espresso.classfile.JavaVersion.VersionRange.VERSION_16_OR_HIGHER; import static com.oracle.truffle.espresso.classfile.JavaVersion.VersionRange.VERSION_17_OR_HIGHER; import static com.oracle.truffle.espresso.classfile.JavaVersion.VersionRange.VERSION_19_OR_HIGHER; @@ -432,35 +431,6 @@ public Meta(EspressoContext context) { java_nio_file_NotDirectoryException = knownKlass(Types.java_nio_file_NotDirectoryException); java_nio_file_NotLinkException = knownKlass(Types.java_nio_file_NotLinkException); - if (context.getLanguage().useEspressoLibs()) { - java_util_zip_CRC32 = knownKlass(Types.java_util_zip_CRC32); - HIDDEN_CRC32 = diff().field(ALL, Names.HIDDEN_CRC32, Types._int).maybeHiddenfield(java_util_zip_CRC32); - } else { - java_util_zip_CRC32 = null; - HIDDEN_CRC32 = null; - } - - java_util_zip_Inflater = knownKlass(Types.java_util_zip_Inflater); - if (getJavaVersion().java11OrLater()) { - java_util_zip_Inflater_inputConsumed = java_util_zip_Inflater.requireDeclaredField(Names.inputConsumed, Types._int); - java_util_zip_Inflater_outputConsumed = java_util_zip_Inflater.requireDeclaredField(Names.outputConsumed, Types._int); - java_util_zip_Inflater_buf = null; - java_util_zip_Inflater_len = null; - java_util_zip_Inflater_off = null; - java_util_zip_Inflater_needDict = null; - java_util_zip_Inflater_finished = null; - } else { - java_util_zip_Inflater_inputConsumed = null; - java_util_zip_Inflater_outputConsumed = null; - java_util_zip_Inflater_buf = java_util_zip_Inflater.requireDeclaredField(Names.buf, Types._byte_array); - java_util_zip_Inflater_len = java_util_zip_Inflater.requireDeclaredField(Names.len, Types._int); - java_util_zip_Inflater_off = java_util_zip_Inflater.requireDeclaredField(Names.off, Types._int); - java_util_zip_Inflater_needDict = java_util_zip_Inflater.requireDeclaredField(Names.needDict, Types._boolean); - java_util_zip_Inflater_finished = java_util_zip_Inflater.requireDeclaredField(Names.finished, Types._boolean); - } - - java_util_zip_DataFormatException = knownKlass(Types.java_util_zip_DataFormatException); - ObjectKlass nioNativeThreadKlass = knownKlass(Types.sun_nio_ch_NativeThread); sun_nio_ch_NativeThread_init = nioNativeThreadKlass.lookupDeclaredMethod(Names.init, Signatures._void); if (getJavaVersion().java21OrLater()) { @@ -1666,19 +1636,6 @@ private DiffVersionLoadHelper diff() { public final ObjectKlass java_nio_file_InvalidPathException; public final ObjectKlass java_nio_file_NotLinkException; - public final ObjectKlass java_util_zip_CRC32; - public final Field HIDDEN_CRC32; - - public final ObjectKlass java_util_zip_Inflater; - public final Field java_util_zip_Inflater_inputConsumed; - public final Field java_util_zip_Inflater_outputConsumed; - public final Field java_util_zip_Inflater_buf; - public final Field java_util_zip_Inflater_len; - public final Field java_util_zip_Inflater_off; - public final Field java_util_zip_Inflater_needDict; - public final Field java_util_zip_Inflater_finished; - public final ObjectKlass java_util_zip_DataFormatException; - public final Method sun_nio_ch_NativeThread_isNativeThread; public final Method sun_nio_ch_NativeThread_current0; public final Method sun_nio_ch_NativeThread_signal; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/Classpath.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/Classpath.java index 80abd50ca997..6207dd8c18ee 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/Classpath.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/Classpath.java @@ -22,6 +22,8 @@ */ package com.oracle.truffle.espresso.runtime; +import static java.util.zip.ZipFile.OPEN_READ; + import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; @@ -31,6 +33,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.jar.JarFile; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -67,7 +70,7 @@ public static ClasspathEntry createEntry(String name) { } } try { - ZipFile zipFile = new ZipFile(pathFile); + ZipFile zipFile = new JarFile(pathFile, false, OPEN_READ, context.getJavaVersion().toRunTimeVersion()); return new Archive(pathFile, zipFile); } catch (IOException ignored) { } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java index 0e05673f798d..ee38043ed917 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java @@ -102,6 +102,7 @@ import com.oracle.truffle.espresso.io.TruffleIO; import com.oracle.truffle.espresso.jni.JNIHandles; import com.oracle.truffle.espresso.jni.JniEnv; +import com.oracle.truffle.espresso.libs.LibsMeta; import com.oracle.truffle.espresso.libs.LibsState; import com.oracle.truffle.espresso.meta.EspressoError; import com.oracle.truffle.espresso.meta.Meta; @@ -203,6 +204,7 @@ public final class EspressoContext implements RuntimeAccess Date: Fri, 25 Jul 2025 15:04:05 +0200 Subject: [PATCH 02/26] Substitution for the TruffleFileSystem: - For the native methods we generally extract the string name from the guest file and convert this string to a TruffleFile which is than used to implement the semantics - If in the native mehtod we are given a TrufflePath we can extract TruffleFile from a hidden field. - NewFileChannelHelper to cope with internal API changes of FileChannelImpl.open - Introduce a helper class for parsing file attributes --- .../src/sun/nio/fs/NewFileChannelHelper.java | 37 +++ .../src/sun/nio/fs/NewFileChannelHelper.java | 37 +++ .../src/java/io/TruffleFileSystem.java | 10 +- .../src/sun/nio/fs/DummyWatchService.java | 40 +++ .../src/sun/nio/fs/FileAttributeParser.java | 91 +++++++ .../src/sun/nio/fs/NewFileChannelHelper.java | 43 ++++ .../nio/fs/TruffleBasicFileAttributeView.java | 3 +- .../nio/fs/TruffleBasicFileAttributes.java | 3 +- .../src/sun/nio/fs/TruffleFileSystem.java | 6 +- .../sun/nio/fs/TruffleFileSystemProvider.java | 69 +++-- .../src/sun/nio/fs/TrufflePath.java | 2 +- .../espresso/descriptors/EspressoSymbols.java | 14 ++ .../oracle/truffle/espresso/io/FDAccess.java | 52 +++- .../com/oracle/truffle/espresso/io/Throw.java | 94 +++++-- .../oracle/truffle/espresso/io/TruffleIO.java | 153 ++++++++++- .../impl/Target_java_io_FileDescriptor.java | 8 +- .../impl/Target_java_io_FileInputStream.java | 27 +- .../impl/Target_java_io_FileOutputStream.java | 22 +- .../impl/Target_java_io_RandomAccessFile.java | 16 +- .../Target_java_io_TruffleFileSystem.java | 112 ++++++++- .../Target_sun_nio_ch_FileDispatcherImpl.java | 101 ++++++-- ..._sun_nio_fs_TruffleFileSystemProvider.java | 237 +++++++++++++++--- .../espresso/runtime/EspressoContext.java | 1 + 23 files changed, 1014 insertions(+), 164 deletions(-) create mode 100644 espresso/src/com.oracle.truffle.espresso.io.jdk21/src/sun/nio/fs/NewFileChannelHelper.java create mode 100644 espresso/src/com.oracle.truffle.espresso.io.jdk25/src/sun/nio/fs/NewFileChannelHelper.java create mode 100644 espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/DummyWatchService.java create mode 100644 espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/FileAttributeParser.java create mode 100644 espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/NewFileChannelHelper.java diff --git a/espresso/src/com.oracle.truffle.espresso.io.jdk21/src/sun/nio/fs/NewFileChannelHelper.java b/espresso/src/com.oracle.truffle.espresso.io.jdk21/src/sun/nio/fs/NewFileChannelHelper.java new file mode 100644 index 000000000000..fce795b8acb0 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.io.jdk21/src/sun/nio/fs/NewFileChannelHelper.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 sun.nio.fs; + +import java.io.Closeable; +import java.io.FileDescriptor; +import java.nio.channels.FileChannel; + +import sun.nio.ch.FileChannelImpl; + +public class NewFileChannelHelper { + public static FileChannel open(FileDescriptor fd, String path, + boolean readable, boolean writable, + boolean sync, boolean direct, Closeable parent) { + return FileChannelImpl.open(fd, path, readable, writable, direct, parent); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.io.jdk25/src/sun/nio/fs/NewFileChannelHelper.java b/espresso/src/com.oracle.truffle.espresso.io.jdk25/src/sun/nio/fs/NewFileChannelHelper.java new file mode 100644 index 000000000000..5a497dfc140d --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.io.jdk25/src/sun/nio/fs/NewFileChannelHelper.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 sun.nio.fs; + +import java.io.Closeable; +import java.io.FileDescriptor; +import java.nio.channels.FileChannel; + +import sun.nio.ch.FileChannelImpl; + +public class NewFileChannelHelper { + public static FileChannel open(FileDescriptor fd, String path, + boolean readable, boolean writable, + boolean sync, boolean direct, Closeable parent) { + return FileChannelImpl.open(fd, path, readable, writable, sync, direct, parent); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/java/io/TruffleFileSystem.java b/espresso/src/com.oracle.truffle.espresso.io/src/java/io/TruffleFileSystem.java index bfc60c28c6f5..66017229dede 100644 --- a/espresso/src/com.oracle.truffle.espresso.io/src/java/io/TruffleFileSystem.java +++ b/espresso/src/com.oracle.truffle.espresso.io/src/java/io/TruffleFileSystem.java @@ -71,7 +71,13 @@ public String getDefaultParent() { @Override public String fromURIPath(String path) { - return fromURIPath0(path); + // copy pasted from UnixFileSystem + String p = path; + if (p.endsWith("/") && (p.length() > 1)) { + // "/foo/" --> "/foo", but "/" --> "/" + p = p.substring(0, p.length() - 1); + } + return p; } @Override @@ -164,7 +170,7 @@ public long getSpace(File f, int t) { return getSpace0(f, t); } - // @Override is defined in 11+ + @Override public int getNameMax(String path) { return getNameMax0(path); } diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/DummyWatchService.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/DummyWatchService.java new file mode 100644 index 000000000000..03c9aa17f939 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/DummyWatchService.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 sun.nio.fs; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.WatchEvent; +import java.nio.file.WatchKey; + +class DummyWatchService extends AbstractWatchService { + @Override + WatchKey register(Path path, WatchEvent.Kind[] events, WatchEvent.Modifier... modifiers) throws IOException { + return null; + } + + @Override + void implClose() throws IOException { + + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/FileAttributeParser.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/FileAttributeParser.java new file mode 100644 index 000000000000..14006c934da4 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/FileAttributeParser.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 sun.nio.fs; + +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.PosixFilePermission; +import java.util.Arrays; +import java.util.Set; + +class FileAttributeParser { + static final int OWNER_READ_VALUE = 256; + static final int OWNER_WRITE_VALUE = 128; + static final int OWNER_EXECUTE_VALUE = 64; + static final int GROUP_READ_VALUE = 32; + static final int GROUP_WRITE_VALUE = 16; + static final int GROUP_EXECUTE_VALUE = 8; + static final int OTHERS_READ_VALUE = 4; + static final int OTHERS_WRITE_VALUE = 2; + static final int OTHERS_EXECUTE_VALUE = 1; + + static int parse(FileAttribute... attrs) { + if (attrs != null) { + for (FileAttribute attr : attrs) { + if (attr.name().equals("posix:permissions")) { + @SuppressWarnings("unchecked") + Set perms = (Set) attr.value(); + return getMaskfromPosix(perms); + } else { + throw new UnsupportedOperationException("file attributes: " + Arrays.toString(attrs)); + } + } + } + return 0; + } + + private static int getMaskfromPosix(Set perms) { + int mask = 0; + for (PosixFilePermission perm : perms) { + switch (perm) { + case OWNER_READ: + mask |= OWNER_READ_VALUE; + break; + case OWNER_WRITE: + mask |= OWNER_WRITE_VALUE; + break; + case OWNER_EXECUTE: + mask |= OWNER_EXECUTE_VALUE; + break; + case GROUP_READ: + mask |= GROUP_READ_VALUE; + break; + case GROUP_WRITE: + mask |= GROUP_WRITE_VALUE; + break; + case GROUP_EXECUTE: + mask |= GROUP_EXECUTE_VALUE; + break; + case OTHERS_READ: + mask |= OTHERS_READ_VALUE; + break; + case OTHERS_WRITE: + mask |= OTHERS_WRITE_VALUE; + break; + case OTHERS_EXECUTE: + mask |= OTHERS_EXECUTE_VALUE; + break; + } + } + return mask; + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/NewFileChannelHelper.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/NewFileChannelHelper.java new file mode 100644 index 000000000000..b37a4841dd11 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/NewFileChannelHelper.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 sun.nio.fs; + +import java.io.Closeable; +import java.io.FileDescriptor; +import java.nio.channels.FileChannel; +import java.nio.file.OpenOption; +import java.util.Set; + +public class NewFileChannelHelper { + public static FileChannel open(FileDescriptor fd, String path, + boolean readable, boolean writable, + boolean sync, boolean direct, Closeable parent) { + // should be implemented in the overlay project since its version specific. + throw new IllegalStateException("Should not reach here!"); + } + + public static boolean getDirectOption(Set options) { + // should be implemented in the overlay project since its version specific. + throw new IllegalStateException("Should not reach here!"); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleBasicFileAttributeView.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleBasicFileAttributeView.java index 426cb746a19a..0ce1b960983c 100644 --- a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleBasicFileAttributeView.java +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleBasicFileAttributeView.java @@ -26,7 +26,6 @@ import java.nio.file.attribute.BasicFileAttributeView; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -71,7 +70,7 @@ Map readAttributes(String attributes) throws IOException { TruffleBasicFileAttributes bfa = (TruffleBasicFileAttributes) readAttributes(); List queriedAttributes = "*".equals(attributes) ? TruffleBasicFileAttributes.BASIC_ATTRIBUTES - : Arrays.asList(attributes.split(",")); + : List.of(attributes.split(",")); HashMap map = new HashMap<>(); for (String attributeName : queriedAttributes) { diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleBasicFileAttributes.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleBasicFileAttributes.java index 98a59bd1e1cf..ba5d18977ed8 100644 --- a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleBasicFileAttributes.java +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleBasicFileAttributes.java @@ -24,7 +24,6 @@ import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; -import java.util.Arrays; import java.util.List; /** @@ -36,7 +35,7 @@ */ final class TruffleBasicFileAttributes implements BasicFileAttributes { - static final List BASIC_ATTRIBUTES = Arrays.asList( + static final List BASIC_ATTRIBUTES = List.of( "lastModifiedTime", "lastAccessTime", "creationTime", diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFileSystem.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFileSystem.java index 0a7361277922..195f0ca68b70 100644 --- a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFileSystem.java +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFileSystem.java @@ -183,8 +183,12 @@ public UserPrincipalLookupService getUserPrincipalLookupService() { throw new UnsupportedOperationException(); } + /** + * We cannot just throw {@link UnsupportedOperationException} since we implement the Default + * filesystem. + */ @Override public WatchService newWatchService() throws IOException { - throw new UnsupportedOperationException(); + return new DummyWatchService(); } } diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFileSystemProvider.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFileSystemProvider.java index 57fc76815632..c061c90de497 100644 --- a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFileSystemProvider.java +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFileSystemProvider.java @@ -43,7 +43,6 @@ import java.nio.file.attribute.FileAttribute; import java.nio.file.attribute.FileAttributeView; import java.nio.file.spi.FileSystemProvider; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; @@ -115,7 +114,7 @@ public FileSystem getFileSystem(URI uri) { @Override public Path getPath(URI uri) { - throw new UnsupportedOperationException(); + return new TrufflePath(theFileSystem(), uri.getPath()); } @Override @@ -125,19 +124,49 @@ public SeekableByteChannel newByteChannel(Path path, Set o @Override public FileChannel newFileChannel(Path path, Set options, FileAttribute... attrs) throws IOException { - if (attrs == null || attrs.length > 0) { - throw new UnsupportedOperationException("file attributes: " + Arrays.toString(attrs)); - } + int fileAttributeMask = FileAttributeParser.parse(attrs); int openOptionsMask = openOptionsToMask(options); - return newFileChannel0(TrufflePath.toTrufflePath(path), new FileDescriptor(), openOptionsMask); + boolean readable = options.contains(StandardOpenOption.READ); + boolean sync = options.contains(StandardOpenOption.SYNC); + boolean writable = options.contains(StandardOpenOption.WRITE); + boolean append = options.contains(StandardOpenOption.APPEND); + + // default is reading; append => writing + if (!readable && !writable) { + if (append) { + writable = true; + } else { + readable = true; + } + } + + // set direct option + boolean direct = false; + for (OpenOption option : options) { + if (ExtendedOptions.DIRECT.matches(option)) { + direct = true; + break; + } + } + // check for Exceptions + if (readable && append) { + throw new IllegalArgumentException("READ + APPEND not allowed"); + } + if (append && options.contains(StandardOpenOption.TRUNCATE_EXISTING)) { + throw new IllegalArgumentException("APPEND + TRUNCATE_EXISTING not allowed"); + } + + FileDescriptor fd = new FileDescriptor(); + // populates fd via HostCode which opens the file and checks the permissions + newFileChannel0(TrufflePath.toTrufflePath(path), fd, openOptionsMask, fileAttributeMask); + return NewFileChannelHelper.open(fd, path.toString(), readable, writable, sync, direct, null); } @Override + @SuppressWarnings("unchecked") public void createDirectory(Path dir, FileAttribute... attrs) throws IOException { - if (attrs == null || attrs.length > 0) { - throw new UnsupportedOperationException("file attributes: " + Arrays.toString(attrs)); - } - createDirectory0(TrufflePath.toTrufflePath(dir)); + int fileAttributeMask = FileAttributeParser.parse(attrs); + createDirectory0(TrufflePath.toTrufflePath(dir), fileAttributeMask); } @Override @@ -155,7 +184,7 @@ public void delete(Path path) throws IOException { // TODO(peterssen): Add NO_FOLLOW_LINKS? // Keep in sync with Target_*_TruffleFileSystemProvider#SUPPORTED_OPEN_OPTIONS. - private static final List SUPPORTED_OPEN_OPTIONS = Collections.unmodifiableList(Arrays.asList( + private static final List SUPPORTED_OPEN_OPTIONS = Collections.unmodifiableList(List.of( StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.APPEND, @@ -169,7 +198,7 @@ public void delete(Path path) throws IOException { // TODO(peterssen): Add NO_FOLLOW_LINKS? // Keep in sync with Target_*_TruffleFileSystemProvider#SUPPORTED_COPY_OPTIONS. - private static final List SUPPORTED_COPY_OPTIONS = Collections.unmodifiableList(Arrays.asList( + private static final List SUPPORTED_COPY_OPTIONS = Collections.unmodifiableList(List.of( StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.ATOMIC_MOVE)); @@ -234,11 +263,11 @@ public boolean isHidden(Path path) throws IOException { @Override public FileStore getFileStore(Path path) throws IOException { - throw new UnsupportedOperationException(); + return null; } // Keep in sync with Target_*_TruffleFileSystemProvider#SUPPORTED_ACCESS_MODES. - private static final List SUPPORTED_ACCESS_MODES = Collections.unmodifiableList(Arrays.asList( + private static final List SUPPORTED_ACCESS_MODES = Collections.unmodifiableList(List.of( AccessMode.READ, AccessMode.WRITE, AccessMode.EXECUTE)); @@ -320,10 +349,8 @@ public void setAttribute(Path path, String attribute, Object value, LinkOption.. @Override public void createSymbolicLink(Path link, Path target, FileAttribute... attrs) throws IOException { - if (attrs == null || attrs.length > 0) { - throw new UnsupportedOperationException("file attributes: " + Arrays.toString(attrs)); - } - createSymbolicLink0(TrufflePath.toTrufflePath(link), TrufflePath.toTrufflePath(target)); + int fileAttributeMask = FileAttributeParser.parse(attrs); + createSymbolicLink0(TrufflePath.toTrufflePath(link), TrufflePath.toTrufflePath(target), fileAttributeMask); } @Override @@ -341,9 +368,9 @@ public Path readSymbolicLink(Path link) throws IOException { private static native String getSeparator0(); - private static native FileChannel newFileChannel0(TrufflePath path, FileDescriptor fileDescriptor, int openOptionsMask) throws IOException; + private static native void newFileChannel0(TrufflePath path, FileDescriptor fileDescriptor, int openOptionsMask, int fileAttributeMask) throws IOException; - private static native void createDirectory0(TrufflePath path) throws IOException; + private static native void createDirectory0(TrufflePath path, int fileAttributeMask) throws IOException; private static native void delete0(TrufflePath path) throws IOException; @@ -355,7 +382,7 @@ public Path readSymbolicLink(Path link) throws IOException { private static native void checkAccess0(TrufflePath path, int accessModesMask) throws IOException; - private static native void createSymbolicLink0(TrufflePath link, TrufflePath target) throws IOException; + private static native void createSymbolicLink0(TrufflePath link, TrufflePath target, int fileAttributeMask) throws IOException; private static native void createLink0(TrufflePath link, TrufflePath existing) throws IOException; diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TrufflePath.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TrufflePath.java index 7e408d01c4ea..e265d0bd3804 100644 --- a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TrufflePath.java +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TrufflePath.java @@ -264,7 +264,7 @@ public WatchKey register(WatchService watcher, WatchEvent.Kind... events) thr @Override public Iterator iterator() { - List components = Arrays.asList(getPathComponents()); + List components = List.of(getPathComponents()); return MapFilterIterator.map(components.iterator(), s -> new TrufflePath(getTruffleFileSystem(), s)); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java index 959223e9fd29..59522f7c0c06 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java @@ -109,6 +109,9 @@ public static void ensureInitialized() { public static final Symbol java_io_IOException = SYMBOLS.putType("Ljava/io/IOException;"); public static final Symbol java_io_File = SYMBOLS.putType("Ljava/io/File;"); public static final Symbol java_io_FileNotFoundException = SYMBOLS.putType("Ljava/io/FileNotFoundException;"); + public static final Symbol java_nio_channels_ClosedByInterruptException = SYMBOLS.putType("Ljava/nio/channels/ClosedByInterruptException;"); + public static final Symbol java_nio_channels_AsynchronousCloseException = SYMBOLS.putType("Ljava/nio/channels/AsynchronousCloseException;"); + public static final Symbol java_nio_channels_ClosedChannelException = SYMBOLS.putType("Ljava/nio/channels/ClosedChannelException;"); public static final Symbol java_io_FileDescriptor = SYMBOLS.putType("Ljava/io/FileDescriptor;"); public static final Symbol java_io_FileInputStream = SYMBOLS.putType("Ljava/io/FileInputStream;"); public static final Symbol java_io_FileOutputStream = SYMBOLS.putType("Ljava/io/FileOutputStream;"); @@ -140,6 +143,7 @@ public static void ensureInitialized() { public static final Symbol sun_nio_fs_TruffleFileSystem = SYMBOLS.putType("Lsun/nio/fs/TruffleFileSystem;"); public static final Symbol sun_nio_fs_TruffleFileSystemProvider = SYMBOLS.putType("Lsun/nio/fs/TruffleFileSystemProvider;"); + public static final Symbol sun_nio_fs_FileAttributeParser = SYMBOLS.putType("Lsun/nio/fs/FileAttributeParser;"); public static final Symbol sun_nio_fs_DefaultFileSystemProvider = SYMBOLS.putType("Lsun/nio/fs/DefaultFileSystemProvider;"); public static final Symbol sun_nio_ch_FileChannelImpl = SYMBOLS.putType("Lsun/nio/ch/FileChannelImpl;"); public static final Symbol sun_nio_ch_NativeThread = SYMBOLS.putType("Lsun/nio/ch/NativeThread;"); @@ -756,6 +760,16 @@ public static class Names { // sun.nio.fs.TrufflePath public static final Symbol HIDDEN_TRUFFLE_FILE = SYMBOLS.putName("0HIDDEN_TRUFFLE_FILE"); public static final Symbol instance = SYMBOLS.putName("instance"); + // sun.nio.fs.TruffleFileSystemProvider + public static final Symbol OWNER_READ_VALUE = SYMBOLS.putName("OWNER_READ_VALUE"); + public static final Symbol OWNER_WRITE_VALUE = SYMBOLS.putName("OWNER_WRITE_VALUE"); + public static final Symbol OWNER_EXECUTE_VALUE = SYMBOLS.putName("OWNER_EXECUTE_VALUE"); + public static final Symbol GROUP_READ_VALUE = SYMBOLS.putName("GROUP_READ_VALUE"); + public static final Symbol GROUP_WRITE_VALUE = SYMBOLS.putName("GROUP_WRITE_VALUE"); + public static final Symbol GROUP_EXECUTE_VALUE = SYMBOLS.putName("GROUP_EXECUTE_VALUE"); + public static final Symbol OTHERS_READ_VALUE = SYMBOLS.putName("OTHERS_READ_VALUE"); + public static final Symbol OTHERS_WRITE_VALUE = SYMBOLS.putName("OTHERS_WRITE_VALUE"); + public static final Symbol OTHERS_EXECUTE_VALUE = SYMBOLS.putName("OTHERS_EXECUTE_VALUE"); // java.util.zip public static final Symbol HIDDEN_CRC32 = SYMBOLS.putName("0HIDDEN_CRC32"); public static final Symbol inputConsumed = SYMBOLS.putName("inputConsumed"); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/FDAccess.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/FDAccess.java index 76e770dcab06..fcefe3123798 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/FDAccess.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/FDAccess.java @@ -22,25 +22,59 @@ */ package com.oracle.truffle.espresso.io; -import static com.oracle.truffle.espresso.io.Checks.nullCheck; - import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.RandomAccessFile; -import com.oracle.truffle.espresso.descriptors.EspressoSymbols.Names; -import com.oracle.truffle.espresso.descriptors.EspressoSymbols.Types; -import com.oracle.truffle.espresso.impl.Field; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; import com.oracle.truffle.espresso.substitutions.JavaType; public abstract class FDAccess { - public static FDAccess GENERIC = new FDAccess() { + + private static final FDAccess FILE_DESCRIPTOR = new FDAccess() { @Override public @JavaType(FileDescriptor.class) StaticObject get(@JavaType(Object.class) StaticObject objectWithFD, TruffleIO io) { - nullCheck(objectWithFD, io); - Field f = objectWithFD.getKlass().lookupField(Names.fd, Types.java_io_FileDescriptor); - return f.getObject(objectWithFD); + return objectWithFD; + } + }; + + private static final FDAccess FILE_INPUT_STREAM = new FDAccess() { + @Override + public @JavaType(FileInputStream.class) StaticObject get(@JavaType(Object.class) StaticObject objectWithFD, TruffleIO io) { + return io.java_io_FileInputStream_fd.getObject(objectWithFD); + } + }; + + private static final FDAccess FILE_OUTPUT_STREAM = new FDAccess() { + @Override + public @JavaType(FileDescriptor.class) StaticObject get(@JavaType(Object.class) StaticObject objectWithFD, TruffleIO io) { + return io.java_io_FileOutputStream_fd.getObject(objectWithFD); + } + }; + + private static final FDAccess RANDOM_ACCESS_FILE = new FDAccess() { + @Override + public @JavaType(FileDescriptor.class) StaticObject get(@JavaType(RandomAccessFile.class) StaticObject objectWithFD, TruffleIO io) { + Checks.nullCheck(objectWithFD, io); + return io.java_io_RandomAccessFile_fd.getObject(objectWithFD); } }; public abstract @JavaType(FileDescriptor.class) StaticObject get(@JavaType(Object.class) StaticObject objectWithFD, TruffleIO io); + + public static FDAccess forFileDescriptor() { + return FILE_DESCRIPTOR; + } + + public static FDAccess forFileOutputStream() { + return FILE_OUTPUT_STREAM; + } + + public static FDAccess forFileInputStream() { + return FILE_INPUT_STREAM; + } + + public static FDAccess forRandomAccessFile() { + return RANDOM_ACCESS_FILE; + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/Throw.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/Throw.java index d0a4cf66566c..7607775c8777 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/Throw.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/Throw.java @@ -23,8 +23,16 @@ package com.oracle.truffle.espresso.io; import java.io.IOException; +import java.nio.channels.AsynchronousCloseException; +import java.nio.channels.ClosedByInterruptException; +import java.nio.channels.ClosedChannelException; +import java.nio.file.AccessDeniedException; +import java.nio.file.AtomicMoveNotSupportedException; import java.nio.file.DirectoryNotEmptyException; import java.nio.file.FileAlreadyExistsException; +import java.nio.file.NoSuchFileException; +import java.nio.file.NotDirectoryException; +import java.nio.file.NotLinkException; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.espresso.meta.Meta; @@ -53,10 +61,64 @@ public static EspressoException throwIOException(String message, EspressoContext } public static EspressoException throwIOException(IOException e, EspressoContext context) { - if (e.getClass() != IOException.class) { - context.getLogger().warning(() -> "Not exact translation of IOException: " + e.getClass()); + Class exceptionClass = e.getClass(); + String message = getMessageBoundary(e); + if (exceptionClass == ClosedByInterruptException.class) { + throw throwClosedByInterruptException(message, context); } - throw throwIOException(getMessageBoundary(e), context); + + if (exceptionClass == AsynchronousCloseException.class) { + throw throwAsynchronousCloseException(message, context); + } + + if (exceptionClass == ClosedChannelException.class) { + throw throwClosedChannelException(message, context); + } + + if (exceptionClass == FileAlreadyExistsException.class) { + throw throwFileAlreadyExistsException(message, context); + } + + if (exceptionClass == NoSuchFileException.class) { + throw throwNoSuchFileException(message, context); + } + + if (exceptionClass == DirectoryNotEmptyException.class) { + throw throwDirectoryNotEmptyException(message, context); + } + + if (exceptionClass == AtomicMoveNotSupportedException.class) { + throw throwAtomicMoveNotSupportedException(message, context); + } + + if (exceptionClass == NotLinkException.class) { + throw throwNotLinkException(message, context); + } + + if (exceptionClass == AccessDeniedException.class) { + throw throwAccessDeniedException(message, context); + } + + if (exceptionClass == NotDirectoryException.class) { + throw throwNotDirectoryException(message, context); + } + + if (exceptionClass != IOException.class) { + context.getLogger().warning(() -> "Not exact translation of IOException: " + exceptionClass); + } + throw throwIOException(message, context); + } + + public static EspressoException throwClosedByInterruptException(String message, EspressoContext context) { + throw context.getMeta().throwExceptionWithMessage(context.getTruffleIO().java_nio_channels_ClosedByInterruptException, message); + } + + public static EspressoException throwAsynchronousCloseException(String message, EspressoContext context) { + throw context.getMeta().throwExceptionWithMessage(context.getTruffleIO().java_nio_channels_AsynchronousCloseException, message); + } + + public static EspressoException throwClosedChannelException(String message, EspressoContext context) { + throw context.getMeta().throwExceptionWithMessage(context.getTruffleIO().java_nio_channels_ClosedChannelException, message); } public static EspressoException throwNonReadable(EspressoContext context) { @@ -85,43 +147,43 @@ public static EspressoException throwSecurityException(SecurityException e, Espr throw throwSecurityException(getMessageBoundary(e), context); } - public static EspressoException throwFileAlreadyExists(FileAlreadyExistsException e, EspressoContext context) { + public static EspressoException throwFileAlreadyExistsException(String message, EspressoContext context) { Meta meta = context.getMeta(); - throw meta.throwExceptionWithMessage(meta.java_nio_file_FileAlreadyExistsException, getMessageBoundary(e)); + throw meta.throwExceptionWithMessage(meta.java_nio_file_FileAlreadyExistsException, message); } - public static EspressoException throwDirectoryNotEmpty(DirectoryNotEmptyException e, EspressoContext context) { + public static EspressoException throwDirectoryNotEmptyException(String message, EspressoContext context) { Meta meta = context.getMeta(); - throw meta.throwExceptionWithMessage(meta.java_nio_file_DirectoryNotEmptyException, getMessageBoundary(e)); + throw meta.throwExceptionWithMessage(meta.java_nio_file_DirectoryNotEmptyException, message); } - public static EspressoException throwAtomicMoveNotSupported(EspressoContext context) { + public static EspressoException throwAtomicMoveNotSupportedException(String message, EspressoContext context) { Meta meta = context.getMeta(); - throw meta.throwException(meta.java_nio_file_AtomicMoveNotSupportedException); + throw meta.throwExceptionWithMessage(meta.java_nio_file_AtomicMoveNotSupportedException, message); } - public static EspressoException throwAccessDenied(EspressoContext context) { + public static EspressoException throwAccessDeniedException(String message, EspressoContext context) { Meta meta = context.getMeta(); - throw meta.throwException(meta.java_nio_file_AccessDeniedException); + throw meta.throwExceptionWithMessage(meta.java_nio_file_AccessDeniedException, message); } - public static EspressoException throwNoSuchFile(String filePath, EspressoContext context) { + public static EspressoException throwNoSuchFileException(String filePath, EspressoContext context) { Meta meta = context.getMeta(); throw meta.throwExceptionWithMessage(meta.java_nio_file_NoSuchFileException, filePath); } - public static EspressoException throwNotDirectory(String message, EspressoContext context) { + public static EspressoException throwNotDirectoryException(String message, EspressoContext context) { Meta meta = context.getMeta(); throw meta.throwExceptionWithMessage(meta.java_nio_file_NotDirectoryException, message); } - public static EspressoException throwIllegalState(String message, EspressoContext context) { + public static EspressoException throwIllegalStateException(String message, EspressoContext context) { Meta meta = context.getMeta(); throw meta.throwExceptionWithMessage(meta.java_lang_IllegalStateException, message); } - public static EspressoException throwNotLink(String file, EspressoContext context) { + public static EspressoException throwNotLinkException(String message, EspressoContext context) { Meta meta = context.getMeta(); - throw meta.throwExceptionWithMessage(meta.java_nio_file_NotLinkException, file); + throw meta.throwExceptionWithMessage(meta.java_nio_file_NotLinkException, message); } } 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 d9d09c5de7f7..72d3e0bc52d1 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 @@ -40,6 +40,7 @@ import java.nio.channels.WritableByteChannel; import java.nio.file.OpenOption; import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.FileAttribute; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -53,6 +54,7 @@ import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.espresso.classfile.descriptors.Name; import com.oracle.truffle.espresso.classfile.descriptors.Symbol; +import com.oracle.truffle.espresso.descriptors.EspressoSymbols; import com.oracle.truffle.espresso.descriptors.EspressoSymbols.Names; import com.oracle.truffle.espresso.descriptors.EspressoSymbols.Signatures; import com.oracle.truffle.espresso.descriptors.EspressoSymbols.Types; @@ -85,7 +87,11 @@ public final class TruffleIO implements ContextAccess { // Checkstyle: stop field name check public final ObjectKlass java_io_IOException; + public final ObjectKlass java_nio_file_NoSuchFileException; public final ObjectKlass java_io_FileNotFoundException; + public final ObjectKlass java_nio_channels_ClosedByInterruptException; + public final ObjectKlass java_nio_channels_AsynchronousCloseException; + public final ObjectKlass java_nio_channels_ClosedChannelException; public final ObjectKlass java_io_FileDescriptor; public final Field java_io_FileDescriptor_fd; public final Field java_io_FileDescriptor_append; @@ -113,6 +119,9 @@ public final class TruffleIO implements ContextAccess { public final ObjectKlass sun_nio_fs_DefaultFileSystemProvider; public final Method sun_nio_fs_DefaultFileSystemProvider_instance; + public final ObjectKlass sun_nio_fs_FileAttributeParser; + @CompilationFinal public FileAttributeParser_Sync fileAttributeParserSync; + public final ObjectKlass java_io_FileSystem; public final FileSystem_Sync fileSystemSync; // Checkstyle: resume field name check @@ -183,6 +192,29 @@ public TruffleFile getPublicTruffleFileSafe(String path) { } } + /** + * Opens a file and associates it with the given file descriptor holder. + * + * @param self A file descriptor holder. + * @param fdAccess How to get the file descriptor from the holder. + * @param path The location where the file is opened. + * @param openOptions Options to open the file. + * @param attributes The file attributes atomically set when opening the file. + * @return The file descriptor associated with the file. + */ + @TruffleBoundary + public int open(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess, + TruffleFile path, + Set openOptions, + FileAttribute... attributes) { + StaticObject fileDesc = getFileDesc(self, fdAccess); + int fd = open(path, openOptions, attributes); + boolean append = openOptions.contains(StandardOpenOption.APPEND); + updateFD(fileDesc, fd, append); + return fd; + } + /** * Opens a file and associates it with the given file descriptor holder. * @@ -198,8 +230,8 @@ public int open(@JavaType(Object.class) StaticObject self, String name, Set openOptions) { StaticObject fileDesc = getFileDesc(self, fdAccess); - boolean append = openOptions.contains(StandardOpenOption.APPEND); int fd = open(name, openOptions); + boolean append = openOptions.contains(StandardOpenOption.APPEND); updateFD(fileDesc, fd, append); return fd; } @@ -254,9 +286,38 @@ 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 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. + * @param bytes The byte buffer containing the bytes to write. + * @return The number of bytes written, possibly zero. + * @see java.io.FileOutputStream#write(byte[]) + */ + @TruffleBoundary + public int writeBytes(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess, + ByteBuffer bytes) { + // is append already handled ? + StaticObject fileDesc = getFileDesc(self, fdAccess); + int fd = getFD(fileDesc); + Channel channel = Checks.ensureOpen(getChannel(fd), getContext()); + if (channel instanceof WritableByteChannel writableChannel) { + try { + return writableChannel.write(bytes); + } catch (NonWritableChannelException e) { + throw Throw.throwNonWritable(context); + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } + } else { + throw Throw.throwNonWritable(context); + } + + } + + /** + * Writes buffered bytes to the file associated with the given file descriptor holder. + * + * @param self The file descriptor holder. + * @param fdAccess How to get the file descriptor from the holder. + * @param bytes The ByteBuffer containing the bytes to write. * @return The number of bytes written, possibly zero. * @see java.io.FileOutputStream#write(byte[], int, int) */ @@ -337,6 +398,37 @@ public int readBytes(@JavaType(Object.class) StaticObject self, return readBytes(getFD(fileDesc), bytes, off, len); } + /** + * Reads a byte sequence from the file associated with the given file descriptor holder. + * + * @param self The file descriptor holder. + * @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 + * @see java.io.FileInputStream#read(byte[], int, int) + */ + @TruffleBoundary + public int readBytes(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess, + ByteBuffer buffer) { + StaticObject fileDesc = getFileDesc(self, fdAccess); + int fd = getFD(fileDesc); + Channel channel = Checks.ensureOpen(getChannel(fd), getContext()); + if (channel instanceof ReadableByteChannel readableByteChannel) { + try { + return readableByteChannel.read(buffer); + } catch (NonReadableChannelException e) { + throw Throw.throwNonReadable(context); + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } + } else { + throw Throw.throwNonReadable(context); + } + + } + /** * Reads a byte sequence from the file associated with the given file descriptor. * @@ -411,6 +503,15 @@ public long position(@JavaType(Object.class) StaticObject self, return position(getFD(fileDesc)); } + /** + * Updates the current position of the file-pointer associated with the given file descriptor + * holder. + * + * @param self The file descriptor holder. + * @param fdAccess How to get the file descriptor from the holder. + * @param newPos the new Position + */ + /** * Returns the current position the file associated with the given file descriptor holder is * pointing to. @@ -596,8 +697,13 @@ public TruffleIO(EspressoContext context) { java_io_RandomAccessFile_fd = java_io_RandomAccessFile.requireDeclaredField(Names.fd, Types.java_io_FileDescriptor); rafSync = new RAF_Sync(this); + // IOExceptions java_io_IOException = meta.knownKlass(Types.java_io_IOException); - java_io_FileNotFoundException = meta.knownKlass(Types.java_io_IOException); + java_io_FileNotFoundException = meta.knownKlass(Types.java_io_FileNotFoundException); + java_nio_channels_ClosedByInterruptException = meta.knownKlass(Types.java_nio_channels_ClosedByInterruptException); + java_nio_channels_AsynchronousCloseException = meta.knownKlass(Types.java_nio_channels_AsynchronousCloseException); + java_nio_channels_ClosedChannelException = meta.knownKlass(Types.java_nio_channels_ClosedChannelException); + java_nio_file_NoSuchFileException = meta.knownKlass(Types.java_nio_file_NoSuchFileException); java_io_File = meta.knownKlass(Types.java_io_File); java_io_File_path = java_io_File.requireDeclaredField(Names.path, Types.java_lang_String); @@ -611,6 +717,8 @@ public TruffleIO(EspressoContext context) { sun_nio_fs_DefaultFileSystemProvider = meta.knownKlass(Types.sun_nio_fs_DefaultFileSystemProvider); sun_nio_fs_DefaultFileSystemProvider_instance = sun_nio_fs_DefaultFileSystemProvider.requireDeclaredMethod(Names.instance, Signatures.sun_nio_fs_TruffleFileSystemProvider); + sun_nio_fs_FileAttributeParser = meta.knownKlass(EspressoSymbols.Types.sun_nio_fs_FileAttributeParser); + sun_nio_fs_TrufflePath = meta.knownKlass(Types.sun_nio_fs_TrufflePath); sun_nio_fs_TrufflePath_HIDDEN_TRUFFLE_FILE = sun_nio_fs_TrufflePath.requireHiddenField(Names.HIDDEN_TRUFFLE_FILE); @@ -620,6 +728,13 @@ public TruffleIO(EspressoContext context) { setEnv(context.getEnv()); } + /** + * See {@link Meta#postSystemInit()}. + */ + public void postSystemInit() { + this.fileAttributeParserSync = new FileAttributeParser_Sync(this); + } + private void setEnv(TruffleLanguage.Env env) { synchronized (files) { files.get(FD_STDIN).setNewChannel(new DetachOnCloseInputStream(env.in())); @@ -677,9 +792,9 @@ private int open(TruffleFile path, Channel fc) { } } - private int open(TruffleFile path, Set options) { + private int open(TruffleFile path, Set options, FileAttribute... attributes) { try { - Channel channel = path.newByteChannel(options); + Channel channel = path.newByteChannel(options, attributes); return open(path, channel); } catch (IOException | UnsupportedOperationException | IllegalArgumentException | SecurityException e) { // Guest code only ever expects FileNotFoundException. @@ -806,5 +921,29 @@ public FileSystem_Sync(TruffleIO io) { this.ACCESS_EXECUTE = lookupSyncedValue(io.java_io_FileSystem, Names.ACCESS_EXECUTE); } } + + public static final class FileAttributeParser_Sync { + public final int OWNER_READ_VALUE; + public final int OWNER_WRITE_VALUE; + public final int OWNER_EXECUTE_VALUE; + public final int GROUP_READ_VALUE; + public final int GROUP_WRITE_VALUE; + public final int GROUP_EXECUTE_VALUE; + public final int OTHERS_READ_VALUE; + public final int OTHERS_WRITE_VALUE; + public final int OTHERS_EXECUTE_VALUE; + + public FileAttributeParser_Sync(TruffleIO io) { + this.OWNER_READ_VALUE = lookupSyncedValue(io.sun_nio_fs_FileAttributeParser, Names.OWNER_READ_VALUE); + this.OWNER_WRITE_VALUE = lookupSyncedValue(io.sun_nio_fs_FileAttributeParser, Names.OWNER_WRITE_VALUE); + this.OWNER_EXECUTE_VALUE = lookupSyncedValue(io.sun_nio_fs_FileAttributeParser, Names.OWNER_EXECUTE_VALUE); + this.GROUP_READ_VALUE = lookupSyncedValue(io.sun_nio_fs_FileAttributeParser, Names.GROUP_READ_VALUE); + this.GROUP_WRITE_VALUE = lookupSyncedValue(io.sun_nio_fs_FileAttributeParser, Names.GROUP_WRITE_VALUE); + this.GROUP_EXECUTE_VALUE = lookupSyncedValue(io.sun_nio_fs_FileAttributeParser, Names.GROUP_EXECUTE_VALUE); + this.OTHERS_READ_VALUE = lookupSyncedValue(io.sun_nio_fs_FileAttributeParser, Names.OTHERS_READ_VALUE); + this.OTHERS_WRITE_VALUE = lookupSyncedValue(io.sun_nio_fs_FileAttributeParser, Names.OTHERS_WRITE_VALUE); + this.OTHERS_EXECUTE_VALUE = lookupSyncedValue(io.sun_nio_fs_FileAttributeParser, Names.OTHERS_EXECUTE_VALUE); + } + } // Checkstyle: resume field name check } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_io_FileDescriptor.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_io_FileDescriptor.java index 6c290fcf7f1f..35f9389cc0e4 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_io_FileDescriptor.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_io_FileDescriptor.java @@ -37,12 +37,6 @@ @EspressoSubstitutions(value = FileDescriptor.class, group = LibJava.class) public final class Target_java_io_FileDescriptor { - private static final FDAccess FD = new FDAccess() { - @Override - public @JavaType(FileDescriptor.class) StaticObject get(@JavaType(Object.class) StaticObject objectWithFD, TruffleIO io) { - return objectWithFD; - } - }; @Substitution public static void initIDs() { @@ -65,6 +59,6 @@ public static boolean getAppend(@SuppressWarnings("unused") int fd) { @Throws(IOException.class) public static void close0(@JavaType(FileDescriptor.class) StaticObject self, @Inject TruffleIO io) { - io.close(self, FD); + io.close(self, FDAccess.forFileDescriptor()); } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_io_FileInputStream.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_io_FileInputStream.java index 3f01fca03aa8..1e5e0b8f9a33 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_io_FileInputStream.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_io_FileInputStream.java @@ -43,12 +43,6 @@ @EspressoSubstitutions(value = FileInputStream.class, group = LibJava.class) public final class Target_java_io_FileInputStream { - private static final FDAccess FD = new FDAccess() { - @Override - public @JavaType(FileInputStream.class) StaticObject get(@JavaType(Object.class) StaticObject objectWithFD, TruffleIO io) { - return io.java_io_FileInputStream_fd.getObject(objectWithFD); - } - }; private static final EnumSet READ_ONLY_OPTION_SET = EnumSet.of(StandardOpenOption.READ); @@ -63,14 +57,14 @@ public static void open0(@JavaType(FileInputStream.class) StaticObject self, @JavaType(String.class) StaticObject name, @Inject Meta meta, @Inject TruffleIO io) { Checks.nullCheck(name, meta); - io.open(self, FD, meta.toHostString(name), READ_ONLY_OPTION_SET); + io.open(self, FDAccess.forFileInputStream(), meta.toHostString(name), READ_ONLY_OPTION_SET); } @Substitution(hasReceiver = true) @Throws(IOException.class) public static int read0(@JavaType(FileInputStream.class) StaticObject self, @Inject TruffleIO io) { - return io.readSingle(self, FD); + return io.readSingle(self, FDAccess.forFileInputStream()); } @Substitution(hasReceiver = true) @@ -78,19 +72,19 @@ public static int read0(@JavaType(FileInputStream.class) StaticObject self, public static int readBytes(@JavaType(FileInputStream.class) StaticObject self, @JavaType(byte[].class) StaticObject b, int off, int len, @Inject TruffleIO io) { Checks.nullCheck(b, io); - return io.readBytes(self, FD, b.unwrap(io.getLanguage()), off, len); + return io.readBytes(self, FDAccess.forFileInputStream(), b.unwrap(io.getLanguage()), off, len); } @Substitution(hasReceiver = true) @Throws(IOException.class) public static long length0(@JavaType(FileInputStream.class) StaticObject self, @Inject TruffleIO io) { - return io.length(self, FD); + return io.length(self, FDAccess.forFileInputStream()); } @Substitution(hasReceiver = true) @Throws(IOException.class) public static long position0(@JavaType(FileInputStream.class) StaticObject self, @Inject TruffleIO io) { - return io.position(self, FD); + return io.position(self, FDAccess.forFileInputStream()); } @Substitution(hasReceiver = true) @@ -102,8 +96,13 @@ public static long skip0(@JavaType(FileInputStream.class) StaticObject self, lon @Substitution(hasReceiver = true) @Throws(IOException.class) - @SuppressWarnings("unused") - public static int available0(@JavaType(FileInputStream.class) StaticObject self) { - throw JavaSubstitution.unimplemented(); + public static int available0(@JavaType(FileInputStream.class) StaticObject self, + @Inject TruffleIO io) { + long size = io.length(self, FDAccess.forFileInputStream()); + long pos = io.position(self, FDAccess.forFileInputStream()); + if (size <= pos) { + return 0; + } + return Math.toIntExact(size - pos); } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_io_FileOutputStream.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_io_FileOutputStream.java index 53bb1d0ef126..c513e6698265 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_io_FileOutputStream.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_io_FileOutputStream.java @@ -22,9 +22,10 @@ */ package com.oracle.truffle.espresso.libs.libjava.impl; -import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.IOException; +import java.nio.file.StandardOpenOption; +import java.util.EnumSet; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.espresso.EspressoLanguage; @@ -32,6 +33,7 @@ import com.oracle.truffle.espresso.io.FDAccess; import com.oracle.truffle.espresso.io.TruffleIO; import com.oracle.truffle.espresso.libs.libjava.LibJava; +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; @@ -42,18 +44,22 @@ @EspressoSubstitutions(value = FileOutputStream.class, group = LibJava.class) public final class Target_java_io_FileOutputStream { - private static final FDAccess FD = new FDAccess() { - @Override - public @JavaType(FileDescriptor.class) StaticObject get(@JavaType(Object.class) StaticObject objectWithFD, TruffleIO io) { - return io.java_io_FileOutputStream_fd.getObject(objectWithFD); - } - }; @Substitution public static void initIDs() { // Do nothing. } + @Substitution(hasReceiver = true) + @TruffleBoundary + public static void open0(@JavaType(FileOutputStream.class) StaticObject self, @JavaType(String.class) StaticObject path, boolean append, @Inject TruffleIO io, @Inject Meta meta) { + EnumSet options = EnumSet.of(StandardOpenOption.WRITE, StandardOpenOption.CREATE); + if (append) { + options.add(StandardOpenOption.APPEND); + } + io.open(self, FDAccess.forFileOutputStream(), meta.toHostString(path), options); + } + @Substitution(hasReceiver = true) @Throws(IOException.class) @TruffleBoundary @@ -61,6 +67,6 @@ static void writeBytes(@JavaType(FileOutputStream.class) StaticObject self, @Jav @Inject EspressoLanguage lang, @Inject TruffleIO io, @Inject EspressoContext ctx) { Checks.nullCheck(b, ctx); Checks.requireNonForeign(b, ctx); - io.writeBytes(self, FD, b.unwrap(lang), off, len); + io.writeBytes(self, FDAccess.forFileOutputStream(), b.unwrap(lang), off, len); } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_io_RandomAccessFile.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_io_RandomAccessFile.java index 48492ae96da5..257119396560 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_io_RandomAccessFile.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_io_RandomAccessFile.java @@ -22,7 +22,6 @@ */ package com.oracle.truffle.espresso.libs.libjava.impl; -import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; @@ -49,13 +48,6 @@ @EspressoSubstitutions(value = RandomAccessFile.class, group = LibJava.class) public final class Target_java_io_RandomAccessFile { - private static final FDAccess FD = new FDAccess() { - @Override - public @JavaType(FileDescriptor.class) StaticObject get(@JavaType(RandomAccessFile.class) StaticObject objectWithFD, TruffleIO io) { - Checks.nullCheck(objectWithFD, io); - return io.java_io_RandomAccessFile_fd.getObject(objectWithFD); - } - }; @Substitution public static void initIDs() { @@ -66,7 +58,7 @@ public static void initIDs() { @Throws(IOException.class) static long length0(@JavaType(RandomAccessFile.class) StaticObject self, @Inject TruffleIO io) { - return io.length(self, FD); + return io.length(self, FDAccess.forRandomAccessFile()); } @Substitution(hasReceiver = true) @@ -76,7 +68,7 @@ public static void open0(@JavaType(RandomAccessFile.class) StaticObject self, @J Checks.nullCheck(name, context); String hostName = context.getMeta().toHostString(name); Set openOptions = getOpenOptions(mode, context, io); - io.open(self, FD, hostName, openOptions); + io.open(self, FDAccess.forRandomAccessFile(), hostName, openOptions); } @Substitution(hasReceiver = true, nameProvider = Append0.class) @@ -85,7 +77,7 @@ static int readBytes(@JavaType(RandomAccessFile.class) StaticObject self, @JavaT @Inject EspressoContext context, @Inject TruffleIO io) { Checks.nullCheck(b, context); Checks.requireNonForeign(b, context); - return io.readBytes(self, FD, b.unwrap(context.getLanguage()), off, len); + return io.readBytes(self, FDAccess.forRandomAccessFile(), b.unwrap(context.getLanguage()), off, len); } @Substitution(hasReceiver = true) @@ -94,7 +86,7 @@ static void seek0(@JavaType(RandomAccessFile.class) StaticObject self, long pos, @Inject EspressoContext context, @Inject TruffleIO io) { assert pos >= 0; Checks.nullCheck(self, context); - io.seek(self, FD, pos); + io.seek(self, FDAccess.forRandomAccessFile(), pos); } @TruffleBoundary diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_io_TruffleFileSystem.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_io_TruffleFileSystem.java index 8e0d96451d50..1275308a9d39 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_io_TruffleFileSystem.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_io_TruffleFileSystem.java @@ -27,11 +27,15 @@ import java.io.File; import java.io.IOException; +import java.nio.file.attribute.PosixFilePermission; +import java.util.Collection; +import java.util.Set; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.TruffleFile; import com.oracle.truffle.espresso.io.Throw; import com.oracle.truffle.espresso.io.TruffleIO; +import com.oracle.truffle.espresso.libs.LibsState; import com.oracle.truffle.espresso.libs.libjava.LibJava; import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.runtime.EspressoContext; @@ -122,12 +126,29 @@ static boolean isInvalid0(@JavaType(File.class) StaticObject f, @Inject TruffleI } @Substitution + @TruffleBoundary @Throws(IOException.class) static @JavaType(String.class) StaticObject canonicalize0(@JavaType(String.class) StaticObject path, @Inject TruffleIO io, @Inject EspressoContext ctx) { + // Work around until canonicalize of TruffleFile works for non-existent paths (GR-29215). nullCheck(path, ctx); Meta meta = ctx.getMeta(); + TruffleFile tf = io.getPublicTruffleFileSafe(meta.toHostString(path)); + return meta.toGuestString(recursiveCanonicalize(tf, ctx).getPath()); + + } + + private static TruffleFile recursiveCanonicalize(TruffleFile tf, EspressoContext ctx) { try { - return meta.toGuestString(io.getPublicTruffleFileSafe(meta.toHostString(path)).getCanonicalFile().getPath()); + if (tf.exists()) { + return tf.getCanonicalFile(); + } + TruffleFile parent = tf.getParent(); + if (parent != null) { + TruffleFile partialPath = recursiveCanonicalize(parent, ctx); + return partialPath.resolve(tf.getName()); + } else { + throw Throw.throwIOException("Canonicalize failed for path: " + tf.getPath(), ctx); + } } catch (IOException e) { throw Throw.throwIOException(e, ctx); } catch (SecurityException e) { @@ -198,9 +219,58 @@ static long getLength0(@JavaType(File.class) StaticObject f, @Substitution @SuppressWarnings("unused") + @TruffleBoundary static boolean setPermission0(@JavaType(File.class) StaticObject f, int access, boolean enable, boolean owneronly, @Inject TruffleIO io, @Inject EspressoContext ctx) { - throw JavaSubstitution.unimplemented(); + String path = getPathFromFile(f, io, ctx); + TruffleFile tf = io.getPublicTruffleFileSafe(path); + try { + Set perms = getPosixPermissions(tf, access, enable, owneronly); + if (perms == null) { + return false; + } + tf.setPosixPermissions(perms); + return true; + } catch (IOException e) { + return false; + } + } + + private static Set getPosixPermissions(TruffleFile tf, int access, boolean enable, boolean owneronly) { + try { + Set perms = tf.getPosixPermissions(); + PosixFilePermission ownerPerm = getPermissionForAccess(access, PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE); + PosixFilePermission groupPerm = getPermissionForAccess(access, PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_WRITE, PosixFilePermission.GROUP_EXECUTE); + PosixFilePermission othersPerm = getPermissionForAccess(access, PosixFilePermission.OTHERS_READ, PosixFilePermission.OTHERS_WRITE, PosixFilePermission.OTHERS_EXECUTE); + if (enable) { + perms.add(ownerPerm); + if (!owneronly) { + perms.add(groupPerm); + perms.add(othersPerm); + } + } else { + perms.remove(ownerPerm); + if (!owneronly) { + perms.remove(groupPerm); + perms.remove(othersPerm); + } + } + return perms; + } catch (IOException e) { + return null; + } + } + + private static PosixFilePermission getPermissionForAccess(int access, PosixFilePermission read, PosixFilePermission write, PosixFilePermission execute) { + return switch (access) { + case 4 -> // FileSystem.ACCESS_READ + read; + case 2 -> // FileSystem.ACCESS_WRITE + write; + case 1 -> // FileSystem.ACCESS_EXECUTE + execute; + default -> throw new UnsupportedOperationException("Unsupported access type"); + }; } @Substitution @@ -222,21 +292,51 @@ static boolean createFileExclusively0(@JavaType(String.class) StaticObject pathn @SuppressWarnings("unused") static boolean delete0(@JavaType(File.class) StaticObject path, @Inject TruffleIO io, @Inject EspressoContext ctx) { - throw JavaSubstitution.unimplemented(); + String strPath = getPathFromFile(path, io, ctx); + TruffleFile tf = io.getPublicTruffleFileSafe(strPath); + try { + tf.delete(); + return true; + } catch (IOException e) { + return false; + } } @Substitution @SuppressWarnings("unused") static @JavaType(String[].class) StaticObject list0(@JavaType(File.class) StaticObject path, @Inject TruffleIO io, @Inject EspressoContext ctx) { - throw JavaSubstitution.unimplemented(); + String strPath = getPathFromFile(path, io, ctx); + TruffleFile tf = io.getPublicTruffleFileSafe(strPath); + try { + Collection ls = tf.list(); + StaticObject[] ret = new StaticObject[ls.size()]; + int i = 0; + for (TruffleFile file : ls) { + String name = file.getName(); + if (name != null) { + ret[i++] = ctx.getMeta().toGuestString(name); + } + } + return ctx.getAllocator().wrapArrayAs(ctx.getMeta().java_lang_String_array, ret); + } catch (IOException e) { + return StaticObject.NULL; + } } @Substitution @SuppressWarnings("unused") static boolean createDirectory0(@JavaType(File.class) StaticObject f, - @Inject TruffleIO io, @Inject EspressoContext ctx) { - throw JavaSubstitution.unimplemented(); + @Inject TruffleIO io, @Inject EspressoContext ctx, @Inject LibsState libsState) { + String path = getPathFromFile(f, io, ctx); + TruffleFile tf = io.getPublicTruffleFileSafe(path); + try { + tf.createDirectory(); + return true; + } catch (IOException e) { + libsState.getLogger().fine(() -> "In TruffleFileSystem.createDirectory0 the following exception was ignored: class = " + e.getClass().toString() + ", message = " + e.getMessage()); + return false; + } } @Substitution 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 56e3d898b470..a4abcdc66dbc 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 @@ -24,10 +24,24 @@ 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.espresso.ffi.nfi.NativeUtils; +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; import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Inject; import com.oracle.truffle.espresso.substitutions.JavaSubstitution; import com.oracle.truffle.espresso.substitutions.JavaType; import com.oracle.truffle.espresso.substitutions.Substitution; @@ -35,16 +49,50 @@ @EspressoSubstitutions(group = LibNio.class) public final class Target_sun_nio_ch_FileDispatcherImpl { + @Substitution - public static long allocationGranularity0() { - throw JavaSubstitution.unimplemented(); + @SuppressWarnings("unused") + public static long allocationGranularity0(@Inject Meta meta) { + // Currently we can not query the Platforms allocation granularity with the Truffle virtual + // filesystem. Considering the allocation granularity is only used when mapping files into + // memory returning 1 is a valid approach since we actually just read the file. + return 1; } @Substitution @Throws(IOException.class) @SuppressWarnings("unused") - public static long map0(@JavaType(FileDescriptor.class) StaticObject fd, int prot, long position, long length, boolean isSync) { - throw JavaSubstitution.unimplemented(); + public static long map0(@JavaType(FileDescriptor.class) StaticObject fd, int prot, long position, long length, boolean isSync, + @Inject TruffleIO io, + @Inject EspressoContext ctx) { + + if (prot == 1) { + // We dont allow public writes since we dont actually map the file. See + // sun.nio.ch.FileChannelImpl.toProt + throw Throw.throwUnsupported("mmap for public writes is not supported at the moment", ctx); + } + @Buffer + TruffleObject buffer = ctx.getNativeAccess().allocateMemory(length); + long addr; + try { + addr = InteropLibrary.getUncached().asPointer(buffer); + } catch (UnsupportedMessageException e) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere(e); + } + 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); + io.readBytes(fd, FDAccess.forFileDescriptor(), byteBuffer); + } finally { + // always reset the position + io.seek(fd, FDAccess.forFileDescriptor(), oldPosition); + } + return addr; } @Substitution @@ -72,9 +120,13 @@ public static long transferFrom0(@JavaType(FileDescriptor.class) StaticObject sr @Substitution @Throws(IOException.class) - @SuppressWarnings("unused") - public static long seek0(@JavaType(FileDescriptor.class) StaticObject fd, long offset) { - throw JavaSubstitution.unimplemented(); + public static long seek0(@JavaType(FileDescriptor.class) StaticObject fd, long offset, + @Inject TruffleIO io) { + if (offset == -1) { + return io.position(fd, FDAccess.forFileDescriptor()); + } + io.seek(fd, FDAccess.forFileDescriptor(), offset); + return 0; } @Substitution @@ -107,16 +159,17 @@ public static void release0(@JavaType(FileDescriptor.class) StaticObject fd, lon @Substitution @Throws(IOException.class) - @SuppressWarnings("unused") - public static long size0(@JavaType(FileDescriptor.class) StaticObject fd) { - throw JavaSubstitution.unimplemented(); + public static long size0(@JavaType(FileDescriptor.class) StaticObject fd, @Inject TruffleIO io) { + return io.length(fd, FDAccess.forFileDescriptor()); } @Substitution @Throws(IOException.class) - @SuppressWarnings("unused") - public static int read0(@JavaType(FileDescriptor.class) StaticObject fd, long address, int len) { - throw JavaSubstitution.unimplemented(); + 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); + } @Substitution @@ -128,9 +181,9 @@ public static int readv0(@JavaType(FileDescriptor.class) StaticObject fd, long a @Substitution @Throws(IOException.class) - @SuppressWarnings("unused") - public static int write0(@JavaType(FileDescriptor.class) StaticObject fd, long address, int len) { - throw JavaSubstitution.unimplemented(); + 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); } @Substitution @@ -149,9 +202,17 @@ public static void close0(@JavaType(FileDescriptor.class) StaticObject fd) { @Substitution @Throws(IOException.class) - @SuppressWarnings("unused") - public static int pread0(@JavaType(FileDescriptor.class) StaticObject fd, long address, int len, long position) { - throw JavaSubstitution.unimplemented(); + 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); + } finally { + io.seek(fd, FDAccess.forFileDescriptor(), oldPos); + } } @Substitution diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_fs_TruffleFileSystemProvider.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_fs_TruffleFileSystemProvider.java index 333604edaf25..6058ea347117 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_fs_TruffleFileSystemProvider.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_fs_TruffleFileSystemProvider.java @@ -26,20 +26,36 @@ import java.io.FileDescriptor; import java.io.IOException; -import java.nio.channels.FileChannel; +import java.nio.file.CopyOption; +import java.nio.file.OpenOption; +import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.TruffleFile; +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.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; -import com.oracle.truffle.espresso.substitutions.JavaSubstitution; import com.oracle.truffle.espresso.substitutions.JavaType; import com.oracle.truffle.espresso.substitutions.Substitution; import com.oracle.truffle.espresso.substitutions.Throws; +/* +Can use ctx.getTruffleIO().getPublicTruffleFileSafe(path) to get a Trufflefile from a path + */ @EspressoSubstitutions(type = "Lsun/nio/fs/TruffleFileSystemProvider;", group = LibNio.class) public final class Target_sun_nio_fs_TruffleFileSystemProvider { @@ -50,89 +66,238 @@ public final class Target_sun_nio_fs_TruffleFileSystemProvider { } @Substitution - @Throws(IOException.class) - @SuppressWarnings("unused") - public static @JavaType(FileChannel.class) StaticObject newFileChannel0( + @TruffleBoundary + public static void newFileChannel0( @JavaType(internalName = TRUFFLE_PATH) StaticObject path, @JavaType(FileDescriptor.class) StaticObject fileDescriptor, - int openOptionsMask) { - throw JavaSubstitution.unimplemented(); + int openOptionsMask, int fileAttributeMask, @Inject TruffleIO io) { + // decode openOptionsMask to avoid guest/host Object passing + Set options = maskToOpenOptions(openOptionsMask); + TruffleFile tf = (TruffleFile) io.sun_nio_fs_TrufflePath_HIDDEN_TRUFFLE_FILE.getHiddenObject(path); + // populate fd + io.open(fileDescriptor, FDAccess.forFileDescriptor(), tf, options, toFileAttribute(fileAttributeMask, io)); } @Substitution + @TruffleBoundary @Throws(IOException.class) - @SuppressWarnings("unused") - public static void createDirectory0(@JavaType(internalName = TRUFFLE_PATH) StaticObject path) { - throw JavaSubstitution.unimplemented(); + public static void createDirectory0(@JavaType(internalName = TRUFFLE_PATH) StaticObject path, int fileAttributeMask, @Inject TruffleIO io, @Inject EspressoContext ctx) { + TruffleFile tf = (TruffleFile) io.sun_nio_fs_TrufflePath_HIDDEN_TRUFFLE_FILE.getHiddenObject(path); + + try { + tf.createDirectory(toFileAttribute(fileAttributeMask, io)); + tf.setPosixPermissions(Set.of(PosixFilePermission.values())); + } catch (IOException e) { + throw Throw.throwIOException(e, ctx); + } + } + + private static FileAttribute[] toFileAttribute(int mask, TruffleIO io) { + if (mask == 0) { + return new FileAttribute[0]; + } + Set perms = new HashSet<>(); + if ((mask & io.fileAttributeParserSync.OWNER_READ_VALUE) != 0) { + perms.add(PosixFilePermission.OWNER_READ); + } + if ((mask & io.fileAttributeParserSync.OWNER_WRITE_VALUE) != 0) { + perms.add(PosixFilePermission.OWNER_WRITE); + } + if ((mask & io.fileAttributeParserSync.OWNER_EXECUTE_VALUE) != 0) { + perms.add(PosixFilePermission.OWNER_EXECUTE); + } + + if ((mask & io.fileAttributeParserSync.GROUP_READ_VALUE) != 0) { + perms.add(PosixFilePermission.GROUP_READ); + } + if ((mask & io.fileAttributeParserSync.GROUP_WRITE_VALUE) != 0) { + perms.add(PosixFilePermission.GROUP_WRITE); + } + if ((mask & io.fileAttributeParserSync.GROUP_EXECUTE_VALUE) != 0) { + perms.add(PosixFilePermission.GROUP_EXECUTE); + } + + if ((mask & io.fileAttributeParserSync.OTHERS_READ_VALUE) != 0) { + perms.add(PosixFilePermission.OTHERS_READ); + } + if ((mask & io.fileAttributeParserSync.OTHERS_WRITE_VALUE) != 0) { + perms.add(PosixFilePermission.OTHERS_WRITE); + } + if ((mask & io.fileAttributeParserSync.OTHERS_EXECUTE_VALUE) != 0) { + perms.add(PosixFilePermission.OTHERS_EXECUTE); + } + return new FileAttribute[]{PosixFilePermissions.asFileAttribute(perms)}; } @Substitution @Throws(IOException.class) - @SuppressWarnings("unused") - public static void delete0(@JavaType(internalName = TRUFFLE_PATH) StaticObject path) { - throw JavaSubstitution.unimplemented(); + public static void delete0(@JavaType(internalName = TRUFFLE_PATH) StaticObject path, @Inject TruffleIO io, @Inject EspressoContext ctx) { + TruffleFile tf = (TruffleFile) io.sun_nio_fs_TrufflePath_HIDDEN_TRUFFLE_FILE.getHiddenObject(path); + try { + tf.delete(); + } catch (IOException e) { + throw Throw.throwIOException(e, ctx); + } } @Substitution + @TruffleBoundary @Throws(IOException.class) - @SuppressWarnings("unused") public static void copy0( @JavaType(internalName = TRUFFLE_PATH) StaticObject source, @JavaType(internalName = TRUFFLE_PATH) StaticObject target, - int copyOptions) { - throw JavaSubstitution.unimplemented(); + int copyOptions, @Inject TruffleIO io, @Inject EspressoContext ctx) { + TruffleFile sourceTf = (TruffleFile) io.sun_nio_fs_TrufflePath_HIDDEN_TRUFFLE_FILE.getHiddenObject(source); + TruffleFile targetTf = (TruffleFile) io.sun_nio_fs_TrufflePath_HIDDEN_TRUFFLE_FILE.getHiddenObject(target); + Set copyOptionsSet = maskToCopyOptions(copyOptions); + try { + sourceTf.copy(targetTf, copyOptionsSet.toArray(new CopyOption[0])); + } catch (IOException e) { + throw Throw.throwIOException(e, ctx); + } } @Substitution + @TruffleBoundary @Throws(IOException.class) - @SuppressWarnings("unused") public static void move0( @JavaType(internalName = TRUFFLE_PATH) StaticObject source, @JavaType(internalName = TRUFFLE_PATH) StaticObject target, - int copyOptions) { - throw JavaSubstitution.unimplemented(); + int copyOptions, @Inject TruffleIO io, @Inject EspressoContext ctx) { + TruffleFile sourceTf = (TruffleFile) io.sun_nio_fs_TrufflePath_HIDDEN_TRUFFLE_FILE.getHiddenObject(source); + TruffleFile targetTf = (TruffleFile) io.sun_nio_fs_TrufflePath_HIDDEN_TRUFFLE_FILE.getHiddenObject(target); + Set copyOptionsSet = maskToCopyOptions(copyOptions); + try { + sourceTf.move(targetTf, copyOptionsSet.toArray(new CopyOption[0])); + } catch (IOException e) { + throw Throw.throwIOException(e, ctx); + } } @Substitution @Throws(IOException.class) - @SuppressWarnings("unused") public static boolean isSameFile0( - @JavaType(internalName = TRUFFLE_PATH) StaticObject path, - @JavaType(internalName = TRUFFLE_PATH) StaticObject path2) { - throw JavaSubstitution.unimplemented(); + @JavaType(internalName = TRUFFLE_PATH) StaticObject path1, + @JavaType(internalName = TRUFFLE_PATH) StaticObject path2, @Inject TruffleIO io, @Inject EspressoContext ctx) { + TruffleFile tf1 = (TruffleFile) io.sun_nio_fs_TrufflePath_HIDDEN_TRUFFLE_FILE.getHiddenObject(path1); + TruffleFile tf2 = (TruffleFile) io.sun_nio_fs_TrufflePath_HIDDEN_TRUFFLE_FILE.getHiddenObject(path2); + try { + return tf1.isSameFile(tf2); + } catch (IOException e) { + throw Throw.throwIOException(e, ctx); + } + } @Substitution + @TruffleBoundary @Throws(IOException.class) - @SuppressWarnings("unused") public static void checkAccess0( @JavaType(internalName = TRUFFLE_PATH) StaticObject path, - int accessModesMask) { - throw JavaSubstitution.unimplemented(); + int accessModesMask, @Inject TruffleIO io, @Inject EspressoContext ctx) { + // TruffleFile does not have a checkAccess API. So we explicit call the method + // corresponding to the accessMode-check. + TruffleFile tf = (TruffleFile) io.sun_nio_fs_TrufflePath_HIDDEN_TRUFFLE_FILE.getHiddenObject(path); + if (!tf.exists()) { + throw Throw.throwIOException("No such file: " + tf.getPath(), ctx); + } + if ((accessModesMask & (1 << 0)) != 0 && !(tf.isReadable())) { + throw Throw.throwIOException("Read Access was denied for path: " + tf.getPath(), ctx); + } + + if ((accessModesMask & (1 << 1)) != 0 && !(tf.isWritable())) { + throw Throw.throwIOException("Write Access was denied for path: " + tf.getPath(), ctx); + } + + if ((accessModesMask & (1 << 2)) != 0 && !(tf.isExecutable())) { + throw Throw.throwIOException("Executable Access was denied for path: " + tf.getPath(), ctx); + } } @Substitution + @TruffleBoundary @Throws(IOException.class) - @SuppressWarnings("unused") public static void createSymbolicLink0( @JavaType(internalName = TRUFFLE_PATH) StaticObject link, - @JavaType(internalName = TRUFFLE_PATH) StaticObject target) { - throw JavaSubstitution.unimplemented(); + @JavaType(internalName = TRUFFLE_PATH) StaticObject target, int fileAttributeMask, @Inject TruffleIO io, @Inject EspressoContext ctx) { + TruffleFile linkTf = (TruffleFile) io.sun_nio_fs_TrufflePath_HIDDEN_TRUFFLE_FILE.getHiddenObject(link); + TruffleFile targetTf = (TruffleFile) io.sun_nio_fs_TrufflePath_HIDDEN_TRUFFLE_FILE.getHiddenObject(target); + try { + linkTf.createSymbolicLink(targetTf, toFileAttribute(fileAttributeMask, io)); + } catch (IOException e) { + throw Throw.throwIOException(e, ctx); + } } @Substitution @Throws(IOException.class) - @SuppressWarnings("unused") public static void createLink0( @JavaType(internalName = TRUFFLE_PATH) StaticObject link, - @JavaType(internalName = TRUFFLE_PATH) StaticObject existing) { - throw JavaSubstitution.unimplemented(); + @JavaType(internalName = TRUFFLE_PATH) StaticObject target, @Inject TruffleIO io, @Inject EspressoContext ctx) { + TruffleFile linkTf = (TruffleFile) io.sun_nio_fs_TrufflePath_HIDDEN_TRUFFLE_FILE.getHiddenObject(link); + TruffleFile targetTf = (TruffleFile) io.sun_nio_fs_TrufflePath_HIDDEN_TRUFFLE_FILE.getHiddenObject(target); + try { + linkTf.createLink(targetTf); + } catch (IOException e) { + throw Throw.throwIOException(e, ctx); + } + } @Substitution - @Throws(IOException.class) - @SuppressWarnings("unused") - public static @JavaType(String.class) StaticObject readSymbolicLink0(@JavaType(internalName = TRUFFLE_PATH) StaticObject link) { - throw JavaSubstitution.unimplemented(); + public static @JavaType(String.class) StaticObject readSymbolicLink0(@JavaType(internalName = TRUFFLE_PATH) StaticObject link, @Inject TruffleIO io, @Inject EspressoContext ctx, + @Inject Meta meta) { + TruffleFile linkTf = (TruffleFile) io.sun_nio_fs_TrufflePath_HIDDEN_TRUFFLE_FILE.getHiddenObject(link); + try { + return meta.toGuestString(linkTf.readSymbolicLink().getPath()); + } catch (IOException e) { + throw Throw.throwIOException(e, ctx); + } } + + // TODO(peterssen): Add NO_FOLLOW_LINKS? + // Keep in sync with TruffleFileSystemProvider#SUPPORTED_COPY_OPTIONS. + private static final List SUPPORTED_OPEN_OPTIONS_HOST = Collections.unmodifiableList(List.of( + StandardOpenOption.READ, + StandardOpenOption.WRITE, + StandardOpenOption.APPEND, + StandardOpenOption.TRUNCATE_EXISTING, + StandardOpenOption.CREATE, + StandardOpenOption.CREATE_NEW, + StandardOpenOption.DELETE_ON_CLOSE, + StandardOpenOption.SPARSE, + StandardOpenOption.SYNC, + StandardOpenOption.DSYNC)); + + // TODO(peterssen): Add NO_FOLLOW_LINKS? + // Keep in sync with TruffleFileSystemProvider#SUPPORTED_COPY_OPTIONS. + private static final List SUPPORTED_COPY_OPTIONS = Collections.unmodifiableList(List.of( + StandardCopyOption.REPLACE_EXISTING, + StandardCopyOption.COPY_ATTRIBUTES, + StandardCopyOption.ATOMIC_MOVE)); + + @TruffleBoundary + public static Set maskToOpenOptions(int openOptionsMask) { + // Use a general Set instead of EnumSet if non-enum options exist + Set options = new HashSet<>(); + for (int i = 0; i < SUPPORTED_OPEN_OPTIONS_HOST.size(); i++) { + if ((openOptionsMask & (1 << i)) != 0) { + options.add(SUPPORTED_OPEN_OPTIONS_HOST.get(i)); + } + } + + return options; + } + + @TruffleBoundary + public static Set maskToCopyOptions(int mask) { + Set options = new HashSet<>(); + for (int i = 0; i < SUPPORTED_COPY_OPTIONS.size(); i++) { + if ((mask & (1 << i)) != 0) { + options.add(SUPPORTED_COPY_OPTIONS.get(i)); + } + } + return options; + } + } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java index ee38043ed917..1aab06e9e2c0 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java @@ -584,6 +584,7 @@ private void spawnVM() throws ContextPatchingException { meta.java_lang_OutOfMemoryError.lookupDeclaredMethod(Names._init_, Signatures._void_String).invokeDirectSpecial(outOfMemoryErrorInstance, meta.toGuestString("VM OutOfMemory")); meta.postSystemInit(); + truffleIO.postSystemInit(); // class redefinition will be enabled if debug mode or if any redefine or retransform // capable java agent is present From 9908dfbbec1c5e57bb215db36dbab5d41c0ee099 Mon Sep 17 00:00:00 2001 From: Elia Trachsel Date: Fri, 25 Jul 2025 15:17:50 +0200 Subject: [PATCH 03/26] Various substitutions for native methods used early on in micronaut Hello-World: - For NativeThread substitutions are nops based (among other things) on the nativeBackendId. Thus we moved determining this id from EspressoContext to EspressoLanguage such that we can then use LanguageFilters for those nops. - Some methods are just linked agains JVM methods e.g. see ConstantPool - For other methods we can just use the public java API e.g. see TimeZone - Continuation is not supported - Momentarily for off-heap memory we forward native host addresses to the guest: See FileDispatcherImpl --- .../truffle/espresso/EspressoLanguage.java | 30 +++ .../espresso/descriptors/EspressoSymbols.java | 1 - .../libjava/impl/Target_java_lang_System.java | 7 +- .../impl/Target_java_util_TimeZone.java | 63 +++++++ ...Target_jdk_internal_loader_BootLoader.java | 19 ++ ...get_jdk_internal_reflect_ConstantPool.java | 171 ++++++++++++++++++ ...t_jdk_internal_vm_ContinuationSupport.java | 35 ++++ .../Target_sun_nio_ch_FileDispatcherImpl.java | 2 +- .../espresso/runtime/EspressoContext.java | 21 +-- .../substitutions/DisableSignals.java | 68 +++++++ ...ava_util_concurrent_atomic_AtomicLong.java | 37 ++++ .../Target_sun_nio_ch_NativeThread.java | 78 ++------ 12 files changed, 442 insertions(+), 90 deletions(-) create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_util_TimeZone.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_jdk_internal_reflect_ConstantPool.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_jdk_internal_vm_ContinuationSupport.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/DisableSignals.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_util_concurrent_atomic_AtomicLong.java diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoLanguage.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoLanguage.java index 761ed17f30eb..d7edcdc62cfb 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoLanguage.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoLanguage.java @@ -68,6 +68,9 @@ import com.oracle.truffle.espresso.classfile.descriptors.TypeSymbols; import com.oracle.truffle.espresso.classfile.descriptors.Utf8Symbols; import com.oracle.truffle.espresso.descriptors.EspressoSymbols; +import com.oracle.truffle.espresso.ffi.nfi.NFIIsolatedNativeAccess; +import com.oracle.truffle.espresso.ffi.nfi.NFINativeAccess; +import com.oracle.truffle.espresso.ffi.nfi.NFISulongNativeAccess; import com.oracle.truffle.espresso.impl.EspressoType; import com.oracle.truffle.espresso.impl.SuppressFBWarnings; import com.oracle.truffle.espresso.meta.EspressoError; @@ -146,6 +149,7 @@ public final class EspressoLanguage extends TruffleLanguage imp @CompilationFinal private boolean internalJvmciEnabled; @CompilationFinal private boolean useEspressoLibs; @CompilationFinal private boolean continuum; + @CompilationFinal private String nativeBackendId; @CompilationFinal private boolean useTRegex; @CompilationFinal private int maxStackTraceDepth; // endregion Options @@ -261,6 +265,7 @@ private void initializeOptions(final TruffleLanguage.Env env) { case graal -> new GraalGuestFieldOffsetStrategy(); }; this.useEspressoLibs = env.getOptions().get(EspressoOptions.UseEspressoLibs); + this.nativeBackendId = setNativeBackendId(env); assert guestFieldOffsetStrategy.name().equals(strategy.name()); } @@ -321,6 +326,26 @@ private void extractDataFrom(EspressoLanguage other) { languageCache.importFrom(other.getLanguageCache()); } + private static String setNativeBackendId(final TruffleLanguage.Env env) { + String nativeBackend; + if (env.getOptions().hasBeenSet(EspressoOptions.NativeBackend)) { + nativeBackend = env.getOptions().get(EspressoOptions.NativeBackend); + } else { + // Pick a sane "default" native backend depending on the platform. + boolean isInPreInit = (boolean) env.getConfig().getOrDefault("preinit", false); + if (isInPreInit || !EspressoOptions.RUNNING_ON_SVM) { + if (OS.getCurrent() == OS.Linux) { + nativeBackend = NFIIsolatedNativeAccess.Provider.ID; + } else { + nativeBackend = NFISulongNativeAccess.Provider.ID; + } + } else { + nativeBackend = NFINativeAccess.Provider.ID; + } + } + return nativeBackend; + } + @Override protected boolean patchContext(EspressoContext context, Env newEnv) { // This check has to be done manually as long as language uses exclusive context sharing @@ -354,6 +379,7 @@ protected boolean areOptionsCompatible(OptionValues oldOptions, OptionValues new isOptionCompatible(newOptions, oldOptions, EspressoOptions.UseTRegex) && isOptionCompatible(newOptions, oldOptions, EspressoOptions.GuestFieldOffsetStrategy) && isOptionCompatible(newOptions, oldOptions, EspressoOptions.UseEspressoLibs) && + isOptionCompatible(newOptions, oldOptions, EspressoOptions.NativeBackend) && isOptionCompatible(newOptions, oldOptions, EspressoOptions.MaxJavaStackTraceDepth); } @@ -600,6 +626,10 @@ public boolean useEspressoLibs() { return useEspressoLibs; } + public String nativeBackendId() { + return nativeBackendId; + } + public boolean isContinuumEnabled() { return continuum; } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java index 59522f7c0c06..c808d4f6db6e 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java @@ -145,7 +145,6 @@ public static void ensureInitialized() { public static final Symbol sun_nio_fs_TruffleFileSystemProvider = SYMBOLS.putType("Lsun/nio/fs/TruffleFileSystemProvider;"); public static final Symbol sun_nio_fs_FileAttributeParser = SYMBOLS.putType("Lsun/nio/fs/FileAttributeParser;"); public static final Symbol sun_nio_fs_DefaultFileSystemProvider = SYMBOLS.putType("Lsun/nio/fs/DefaultFileSystemProvider;"); - public static final Symbol sun_nio_ch_FileChannelImpl = SYMBOLS.putType("Lsun/nio/ch/FileChannelImpl;"); public static final Symbol sun_nio_ch_NativeThread = SYMBOLS.putType("Lsun/nio/ch/NativeThread;"); public static final Symbol jdk_internal_loader_ClassLoaders = SYMBOLS.putType("Ljdk/internal/loader/ClassLoaders;"); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_System.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_System.java index f8b2cdaba667..a80fc59bad32 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_System.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_System.java @@ -29,6 +29,7 @@ import com.oracle.truffle.espresso.EspressoLanguage; import com.oracle.truffle.espresso.descriptors.EspressoSymbols.Types; import com.oracle.truffle.espresso.impl.ObjectKlass; +import com.oracle.truffle.espresso.io.TruffleIO; import com.oracle.truffle.espresso.libs.libjava.LibJava; import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.runtime.EspressoContext; @@ -77,7 +78,7 @@ public static final class Raw { @Substitution @TruffleBoundary - public static @JavaType(String[].class) StaticObject platformProperties(@Inject EspressoContext ctx) { + public static @JavaType(String[].class) StaticObject platformProperties(@Inject EspressoContext ctx, @Inject TruffleIO io) { // Import properties from host. Props props = new Props(ctx); String[] known = new String[props.fixedLength]; @@ -99,8 +100,8 @@ public static final class Raw { known[props.osArchNdx] = java.lang.System.getProperty("os.arch"); known[props.osVersionNdx] = java.lang.System.getProperty("os.version"); known[props.lineSeparatorNdx] = java.lang.System.getProperty("line.separator"); - known[props.fileSeparatorNdx] = java.lang.System.getProperty("file.separator"); - known[props.pathSeparatorNdx] = java.lang.System.getProperty("path.separator"); + known[props.fileSeparatorNdx] = String.valueOf(io.getFileSeparator()); + known[props.pathSeparatorNdx] = String.valueOf(io.getPathSeparator()); known[props.javaIoTmpdirNdx] = java.lang.System.getProperty("java.io.tmpdir"); known[props.httpProxyHostNdx] = java.lang.System.getProperty("http.proxyHost"); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_util_TimeZone.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_util_TimeZone.java new file mode 100644 index 000000000000..2dfdf5557afa --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_util_TimeZone.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libjava.impl; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.util.TimeZone; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.espresso.libs.libjava.LibJava; +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; +import com.oracle.truffle.espresso.substitutions.JavaType; +import com.oracle.truffle.espresso.substitutions.Substitution; + +@EspressoSubstitutions(value = TimeZone.class, group = LibJava.class) +public final class Target_java_util_TimeZone { + @Substitution + @TruffleBoundary + @SuppressWarnings("unused") + public static @JavaType(String.class) StaticObject getSystemTimeZoneID(@JavaType(String.class) StaticObject javaHome, @Inject Meta meta, @Inject EspressoContext context) { + return meta.toGuestString(context.getEnv().getTimeZone().getId()); + } + + @Substitution + @TruffleBoundary + public static @JavaType(String.class) StaticObject getSystemGMTOffsetID(@Inject Meta meta, @Inject EspressoContext context) { + ZoneId zone = context.getEnv().getTimeZone(); + String offsetId; + if (zone instanceof ZoneOffset) { + offsetId = zone.getId(); + } else { + ZoneOffset offset = zone.getRules().getOffset(Instant.now()); + offsetId = offset.getId(); + } + return meta.toGuestString(offsetId); + + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_jdk_internal_loader_BootLoader.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_jdk_internal_loader_BootLoader.java index 1fe799e04227..3eef64f43ec4 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_jdk_internal_loader_BootLoader.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_jdk_internal_loader_BootLoader.java @@ -23,12 +23,14 @@ package com.oracle.truffle.espresso.libs.libjava.impl; import com.oracle.truffle.espresso.libs.libjava.LibJava; +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; import com.oracle.truffle.espresso.substitutions.JavaType; import com.oracle.truffle.espresso.substitutions.Substitution; +import com.oracle.truffle.espresso.vm.VM; @EspressoSubstitutions(type = "Ljdk/internal/loader/BootLoader;", group = LibJava.class) public final class Target_jdk_internal_loader_BootLoader { @@ -36,4 +38,21 @@ public final class Target_jdk_internal_loader_BootLoader { public static void setBootLoaderUnnamedModule0(@JavaType(Module.class) StaticObject module, @Inject EspressoContext ctx) { ctx.getVM().JVM_SetBootLoaderUnnamedModule(module); } + @Substitution + public static @JavaType(String[].class) StaticObject getSystemPackageNames(@Inject VM vm, @Inject Meta meta) { + return vm.JVM_GetSystemPackages(meta); + } + + /** + * Returns the location of the package of the given name, if + * defined by the boot loader; otherwise {@code null} is returned. + * + * The location may be a module from the runtime image or exploded image, + * or from the boot class append path (i.e. -Xbootclasspath/a or + * BOOT-CLASS-PATH attribute specified in java agent). + */ + @Substitution + public static @JavaType(String.class) StaticObject getSystemPackageLocation(@JavaType(String[].class) StaticObject name, @Inject VM vm, @Inject Meta meta) { + return vm.JVM_GetSystemPackage(name, meta); + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_jdk_internal_reflect_ConstantPool.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_jdk_internal_reflect_ConstantPool.java new file mode 100644 index 000000000000..676de96822f0 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_jdk_internal_reflect_ConstantPool.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libjava.impl; + +import java.lang.reflect.Field; +import java.lang.reflect.Member; + +import com.oracle.truffle.espresso.libs.libjava.LibJava; +import com.oracle.truffle.espresso.meta.Meta; +import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Inject; +import com.oracle.truffle.espresso.substitutions.JavaSubstitution; +import com.oracle.truffle.espresso.substitutions.JavaType; +import com.oracle.truffle.espresso.substitutions.Substitution; +import com.oracle.truffle.espresso.substitutions.SubstitutionProfiler; +import com.oracle.truffle.espresso.vm.VM; + +@EspressoSubstitutions(type = "Ljdk/internal/reflect/ConstantPool;", group = LibJava.class) +public final class Target_jdk_internal_reflect_ConstantPool { + @Substitution(hasReceiver = true) + public static int getSize0(@JavaType(Object.class) StaticObject unused, @JavaType(Object.class) StaticObject jcpool, @Inject VM vm) { + return vm.JVM_ConstantPoolGetSize(unused, jcpool); + } + + @Substitution(hasReceiver = true) + public static @JavaType(Class.class) StaticObject getClassAt0(@JavaType(Object.class) StaticObject unused, @JavaType(Object.class) StaticObject jcpool, int index, + @Inject VM vm, + @Inject Meta meta, + @Inject SubstitutionProfiler profiler) { + return vm.JVM_ConstantPoolGetClassAt(unused, jcpool, index, meta, profiler); + } + + @Substitution(hasReceiver = true) + @SuppressWarnings("unused") + public static @JavaType(Class.class) StaticObject getClassAtIfLoaded0(@JavaType(Object.class) StaticObject unused, @JavaType(Object.class) StaticObject jcpool, int index) { + throw JavaSubstitution.unimplemented(); + + } + + @Substitution(hasReceiver = true) + @SuppressWarnings("unused") + public static int getClassRefIndexAt0(@JavaType(Object.class) StaticObject unused, @JavaType(Object.class) StaticObject jcpool, int index) { + throw JavaSubstitution.unimplemented(); + + } + + @Substitution(hasReceiver = true) + @SuppressWarnings("unused") + public static @JavaType(Member.class) StaticObject getMethodAt0(@JavaType(Object.class) StaticObject unused, @JavaType(Object.class) StaticObject jcpool, int index) { + throw JavaSubstitution.unimplemented(); + + } + + @Substitution(hasReceiver = true) + @SuppressWarnings("unused") + public static @JavaType(Member.class) StaticObject getMethodAtIfLoaded0(@JavaType(Object.class) StaticObject unused, @JavaType(Object.class) StaticObject jcpool, int index) { + throw JavaSubstitution.unimplemented(); + + } + + @Substitution(hasReceiver = true) + @SuppressWarnings("unused") + public static @JavaType(Field.class) StaticObject getFieldAt0(@JavaType(Object.class) StaticObject unused, @JavaType(Object.class) StaticObject jcpool, int index) { + throw JavaSubstitution.unimplemented(); + + } + + @Substitution(hasReceiver = true) + @SuppressWarnings("unused") + public static @JavaType(Field.class) StaticObject getFieldAtIfLoaded0(@JavaType(Object.class) StaticObject unused, @JavaType(Object.class) StaticObject jcpool, int index) { + throw JavaSubstitution.unimplemented(); + + } + + @Substitution(hasReceiver = true) + @SuppressWarnings("unused") + public static @JavaType(String[].class) StaticObject getMemberRefInfoAt0(@JavaType(Object.class) StaticObject unused, @JavaType(Object.class) StaticObject jcpool, int index) { + throw JavaSubstitution.unimplemented(); + + } + + @Substitution(hasReceiver = true) + @SuppressWarnings("unused") + public static int getNameAndTypeRefIndexAt0(@JavaType(Object.class) StaticObject unused, @JavaType(Object.class) StaticObject jcpool, int index) { + throw JavaSubstitution.unimplemented(); + + } + + @Substitution(hasReceiver = true) + @SuppressWarnings("unused") + public static @JavaType(String[].class) StaticObject getNameAndTypeRefInfoAt0(@JavaType(Object.class) StaticObject unused, @JavaType(Object.class) StaticObject jcpool, int index) { + throw JavaSubstitution.unimplemented(); + + } + + @Substitution(hasReceiver = true) + public static int getIntAt0(@JavaType(Object.class) StaticObject unused, @JavaType(Object.class) StaticObject jcpool, int index, + @Inject VM vm, + @Inject Meta meta, + @Inject SubstitutionProfiler profiler) { + return vm.JVM_ConstantPoolGetIntAt(unused, jcpool, index, meta, profiler); + } + + @Substitution(hasReceiver = true) + public static long getLongAt0(@JavaType(Object.class) StaticObject unused, @JavaType(Object.class) StaticObject jcpool, int index, + @Inject VM vm, + @Inject Meta meta, + @Inject SubstitutionProfiler profiler) { + return vm.JVM_ConstantPoolGetLongAt(unused, jcpool, index, meta, profiler); + } + + @Substitution(hasReceiver = true) + public static float getFloatAt0(@JavaType(Object.class) StaticObject unused, @JavaType(Object.class) StaticObject jcpool, int index, + @Inject VM vm, + @Inject Meta meta, + @Inject SubstitutionProfiler profiler) { + return vm.JVM_ConstantPoolGetFloatAt(unused, jcpool, index, meta, profiler); + } + + @Substitution(hasReceiver = true) + public static double getDoubleAt0(@JavaType(Object.class) StaticObject unused, @JavaType(Object.class) StaticObject jcpool, int index, + @Inject VM vm, + @Inject Meta meta, + @Inject SubstitutionProfiler profiler) { + return vm.JVM_ConstantPoolGetDoubleAt(unused, jcpool, index, meta, profiler); + } + + @Substitution(hasReceiver = true) + public static @JavaType(String.class) StaticObject getStringAt0(@JavaType(Object.class) StaticObject unused, @JavaType(Object.class) StaticObject jcpool, int index, + @Inject VM vm, + @Inject Meta meta, + @Inject SubstitutionProfiler profiler) { + return vm.JVM_ConstantPoolGetStringAt(unused, jcpool, index, meta, profiler); + } + + @Substitution(hasReceiver = true) + public static @JavaType(String.class) StaticObject getUTF8At0(@JavaType(Object.class) StaticObject unused, @JavaType(Object.class) StaticObject jcpool, int index, + @Inject VM vm, + @Inject Meta meta, + @Inject SubstitutionProfiler profiler) { + return vm.JVM_ConstantPoolGetUTF8At(unused, jcpool, index, meta, profiler); + } + + @Substitution(hasReceiver = true) + @SuppressWarnings("unused") + public static byte getTagAt0(@JavaType(Object.class) StaticObject unused, @JavaType(Object.class) StaticObject jcpool, int index) { + throw JavaSubstitution.unimplemented(); + + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_jdk_internal_vm_ContinuationSupport.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_jdk_internal_vm_ContinuationSupport.java new file mode 100644 index 000000000000..7b2a7d18af09 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_jdk_internal_vm_ContinuationSupport.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libjava.impl; + +import com.oracle.truffle.espresso.libs.libjava.LibJava; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Substitution; + +@EspressoSubstitutions(type = "Ljdk/internal/vm/ContinuationSupport;", group = LibJava.class) +public final class Target_jdk_internal_vm_ContinuationSupport { + @Substitution + public static boolean isSupported0() { + return false; + } +} 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 a4abcdc66dbc..7e0f34555fab 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 @@ -122,7 +122,7 @@ public static long transferFrom0(@JavaType(FileDescriptor.class) StaticObject sr @Throws(IOException.class) public static long seek0(@JavaType(FileDescriptor.class) StaticObject fd, long offset, @Inject TruffleIO io) { - if (offset == -1) { + if (offset < 0) { return io.position(fd, FDAccess.forFileDescriptor()); } io.seek(fd, FDAccess.forFileDescriptor(), offset); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java index 1aab06e9e2c0..e80fadc60e75 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java @@ -89,9 +89,6 @@ import com.oracle.truffle.espresso.ffi.EspressoLibsNativeAccess; import com.oracle.truffle.espresso.ffi.NativeAccess; import com.oracle.truffle.espresso.ffi.NativeAccessCollector; -import com.oracle.truffle.espresso.ffi.nfi.NFIIsolatedNativeAccess; -import com.oracle.truffle.espresso.ffi.nfi.NFINativeAccess; -import com.oracle.truffle.espresso.ffi.nfi.NFISulongNativeAccess; import com.oracle.truffle.espresso.impl.ClassLoadingEnv; import com.oracle.truffle.espresso.impl.ClassRegistries; import com.oracle.truffle.espresso.impl.Field; @@ -782,23 +779,7 @@ private StaticObject createBindingsLoader(StaticObject systemClassLoader) { } private NativeAccess spawnNativeAccess() { - String nativeBackend; - if (getEnv().getOptions().hasBeenSet(EspressoOptions.NativeBackend)) { - nativeBackend = getEnv().getOptions().get(EspressoOptions.NativeBackend); - } else { - // Pick a sane "default" native backend depending on the platform. - boolean isInPreInit = (boolean) getEnv().getConfig().getOrDefault("preinit", false); - if (isInPreInit || !EspressoOptions.RUNNING_ON_SVM) { - if (OS.getCurrent() == OS.Linux) { - nativeBackend = NFIIsolatedNativeAccess.Provider.ID; - } else { - nativeBackend = NFISulongNativeAccess.Provider.ID; - } - } else { - nativeBackend = NFINativeAccess.Provider.ID; - } - } - + String nativeBackend = language.nativeBackendId(); List available = new ArrayList<>(); for (NativeAccess.Provider provider : NativeAccessCollector.getInstances(NativeAccess.Provider.class)) { available.add(provider.id()); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/DisableSignals.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/DisableSignals.java new file mode 100644 index 000000000000..e7685b8f8897 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/DisableSignals.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2025, 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.substitutions; + +import com.oracle.truffle.espresso.EspressoLanguage; +import com.oracle.truffle.espresso.EspressoOptions; +import com.oracle.truffle.espresso.ffi.nfi.NFISulongNativeAccess; + +/** + * Wrapper class for Language filters to avoid signaling. Prevent the installation of a signal + * handler except on SVM and not with llvm or EspressoLibs. (sulong virtualizes pthread_self but not + * ptrhead_kill) + */ +public class DisableSignals { + // avoid the installation of a signal handler except on SVM and not with llvm or espresso libs + public static boolean standardFilter(EspressoLanguage language) { + return language.useEspressoLibs() || !EspressoOptions.RUNNING_ON_SVM || (language.nativeBackendId().equals(NFISulongNativeAccess.Provider.ID)); + } + + // various filters + public static class StandardFilter implements LanguageFilter { + public static final LanguageFilter INSTANCE = new StandardFilter(); + + @Override + public boolean isValidFor(EspressoLanguage language) { + return standardFilter(language); + } + } + + public static class Version21orLaterFilter implements LanguageFilter { + public static final LanguageFilter INSTANCE = new Version21orLaterFilter(); + + @Override + public boolean isValidFor(EspressoLanguage language) { + return (standardFilter(language)) && (language.getJavaVersion().java21OrLater()); + } + } + + public static class Version17orEarlierFilter implements LanguageFilter { + public static final LanguageFilter INSTANCE = new Version17orEarlierFilter(); + + @Override + public boolean isValidFor(EspressoLanguage language) { + return (standardFilter(language)) && (language.getJavaVersion().java17OrEarlier()); + } + } + +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_util_concurrent_atomic_AtomicLong.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_util_concurrent_atomic_AtomicLong.java new file mode 100644 index 000000000000..cf57e12fe26a --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_util_concurrent_atomic_AtomicLong.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.substitutions.standard; + +import static com.oracle.truffle.espresso.substitutions.SubstitutionFlag.IsTrivial; + +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Substitution; +import com.oracle.truffle.espresso.vm.VM; + +@EspressoSubstitutions +public final class Target_java_util_concurrent_atomic_AtomicLong { + @Substitution(flags = {IsTrivial}, methodName = "VMSupportsCS8") + public static boolean vmSupportsCS8() { + return VM.JVM_SupportsCX8(); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_sun_nio_ch_NativeThread.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_sun_nio_ch_NativeThread.java index 903713869809..3ab67a807afb 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_sun_nio_ch_NativeThread.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_sun_nio_ch_NativeThread.java @@ -22,85 +22,33 @@ */ package com.oracle.truffle.espresso.substitutions.standard; -import com.oracle.truffle.api.dsl.Bind; -import com.oracle.truffle.api.dsl.Cached; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.nodes.DirectCallNode; -import com.oracle.truffle.espresso.EspressoOptions; -import com.oracle.truffle.espresso.ffi.nfi.NFISulongNativeAccess; -import com.oracle.truffle.espresso.runtime.EspressoContext; +import com.oracle.truffle.espresso.substitutions.DisableSignals; import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; import com.oracle.truffle.espresso.substitutions.Substitution; -import com.oracle.truffle.espresso.substitutions.SubstitutionNode; -import com.oracle.truffle.espresso.substitutions.VersionFilter.Java17OrEarlier; -import com.oracle.truffle.espresso.substitutions.VersionFilter.Java21OrLater; @EspressoSubstitutions public final class Target_sun_nio_ch_NativeThread { /* * This doesn't exist on Windows, it just won't match */ - @Substitution - abstract static class Init extends SubstitutionNode { - abstract void execute(); - @Specialization - static void doDefault(@Bind("getContext()") EspressoContext context, - @Cached("create(context.getMeta().sun_nio_ch_NativeThread_init.getCallTargetNoSubstitution())") DirectCallNode original) { - // avoid the installation of a signal handler except on SVM and not with llvm - if (EspressoOptions.RUNNING_ON_SVM && !(context.getNativeAccess() instanceof NFISulongNativeAccess)) { - original.call(); - } - } + @Substitution(languageFilter = DisableSignals.StandardFilter.class) + public static void init() { } - @Substitution(languageFilter = Java21OrLater.class) - abstract static class IsNativeThread extends SubstitutionNode { - abstract boolean execute(long tid); - - @Specialization - static boolean doDefault(long tid, - @Bind("getContext()") EspressoContext context, - @Cached("create(context.getMeta().sun_nio_ch_NativeThread_isNativeThread.getCallTargetNoSubstitution())") DirectCallNode original) { - if (context.getNativeAccess() instanceof NFISulongNativeAccess) { - // sulong virtualizes pthread_self but not ptrhead_kill - // signal to the JDK that we don't support signaling - return false; - } else { - return (boolean) original.call(tid); - } - } + @Substitution(languageFilter = DisableSignals.Version21orLaterFilter.class) + @SuppressWarnings("unused") + public static boolean isNativeThread(long tid) { + return false; } - @Substitution(languageFilter = Java21OrLater.class) - abstract static class Current0 extends SubstitutionNode { - abstract long execute(); - - @Specialization - static long doDefault(@Bind("getContext()") EspressoContext context, - @Cached("create(context.getMeta().sun_nio_ch_NativeThread_current0.getCallTargetNoSubstitution())") DirectCallNode original) { - if (context.getNativeAccess() instanceof NFISulongNativeAccess) { - // sulong virtualizes pthread_self but not ptrhead_kill - // signal to the JDK that we don't support signaling - return 0; - } else { - return (long) original.call(); - } - } + @Substitution(languageFilter = DisableSignals.Version21orLaterFilter.class) + public static long current0() { + return 0; } - @Substitution(languageFilter = Java17OrEarlier.class) - abstract static class Signal extends SubstitutionNode { - abstract void execute(long nt); - - @Specialization - static void doDefault(long nt, - @Bind("getContext()") EspressoContext context, - @Cached("create(context.getMeta().sun_nio_ch_NativeThread_signal.getCallTargetNoSubstitution())") DirectCallNode original) { - // sulong virtualizes pthread_self but not ptrhead_kill - if (!(context.getNativeAccess() instanceof NFISulongNativeAccess)) { - original.call(nt); - } - } + @Substitution(languageFilter = DisableSignals.Version17orEarlierFilter.class) + @SuppressWarnings("unused") + public static void signal(long nt) { } } From 095ded38fa393f5c31a740ddb7924286bfdd2715 Mon Sep 17 00:00:00 2001 From: Elia Trachsel Date: Fri, 25 Jul 2025 15:46:36 +0200 Subject: [PATCH 04/26] Refactor of TruffleIO: - Introduced some getChannel methods to eliminate "instanceof blocks" every time we need to use a channel of - Introduced a shortcut to directly get the fd given the StaticObject and FdAccess - Introduced a default FDAccess in LibsUtil --- .../oracle/truffle/espresso/io/TruffleIO.java | 181 ++++++++---------- .../truffle/espresso/libs/LibsState.java | 12 ++ 2 files changed, 90 insertions(+), 103 deletions(-) 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 72d3e0bc52d1..39c6147b6fe9 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 @@ -265,8 +265,7 @@ public boolean close(@JavaType(Object.class) StaticObject self, @TruffleBoundary public long length(@JavaType(Object.class) StaticObject self, FDAccess fdAccess) { - StaticObject fileDesc = getFileDesc(self, fdAccess); - return length(getFD(fileDesc)); + return length(getFD(self, fdAccess)); } /** @@ -274,11 +273,7 @@ public long length(@JavaType(Object.class) StaticObject self, */ @TruffleBoundary public long length(int fd) { - Channel channel = Checks.ensureOpen(getChannel(fd), getContext()); - if (channel instanceof SeekableByteChannel seekableChannel) { - return sizeImpl(seekableChannel, context); - } - return 0; // unknown + return sizeImpl(getSeekableChannel(fd), context); } /** @@ -294,22 +289,13 @@ public long length(int fd) { public int writeBytes(@JavaType(Object.class) StaticObject self, FDAccess fdAccess, ByteBuffer bytes) { - // is append already handled ? - StaticObject fileDesc = getFileDesc(self, fdAccess); - int fd = getFD(fileDesc); - Channel channel = Checks.ensureOpen(getChannel(fd), getContext()); - if (channel instanceof WritableByteChannel writableChannel) { - try { - return writableChannel.write(bytes); - } catch (NonWritableChannelException e) { - throw Throw.throwNonWritable(context); - } catch (IOException e) { - throw Throw.throwIOException(e, context); - } - } else { + try { + return getWritableChannel(self, fdAccess).write(bytes); + } catch (NonWritableChannelException e) { throw Throw.throwNonWritable(context); + } catch (IOException e) { + throw Throw.throwIOException(e, context); } - } /** @@ -326,8 +312,7 @@ public int writeBytes(@JavaType(Object.class) StaticObject self, FDAccess fdAccess, byte[] bytes, int off, int len) { - StaticObject fileDesc = getFileDesc(self, fdAccess); - return writeBytes(getFD(fileDesc), bytes, off, len); + return writeBytes(getFD(self, fdAccess), bytes, off, len); } /** @@ -339,12 +324,7 @@ public int writeBytes(@JavaType(Object.class) StaticObject self, public int writeBytes(int fd, byte[] bytes, int off, int len) { - Channel channel = Checks.ensureOpen(getChannel(fd), getContext()); - if (channel instanceof WritableByteChannel) { - return writeBytesImpl((WritableByteChannel) channel, bytes, off, len, context); - } else { - throw Throw.throwNonWritable(context); - } + return writeBytesImpl(getWritableChannel(fd), bytes, off, len, context); } /** @@ -358,8 +338,7 @@ public int writeBytes(int fd, @TruffleBoundary public int readSingle(@JavaType(Object.class) StaticObject self, FDAccess fdAccess) { - StaticObject fileDesc = getFileDesc(self, fdAccess); - return readSingle(getFD(fileDesc)); + return readSingle(getFD(self, fdAccess)); } /** @@ -369,12 +348,7 @@ public int readSingle(@JavaType(Object.class) StaticObject self, */ @TruffleBoundary public int readSingle(int fd) { - Channel channel = Checks.ensureOpen(getChannel(fd), getContext()); - if (channel instanceof ReadableByteChannel readableByteChannel) { - return readSingleImpl(readableByteChannel, context); - } else { - throw Throw.throwNonReadable(context); - } + return readSingleImpl(getReadableChannel(fd), context); } /** @@ -394,8 +368,7 @@ public int readBytes(@JavaType(Object.class) StaticObject self, FDAccess fdAccess, byte[] bytes, int off, int len) { - StaticObject fileDesc = getFileDesc(self, fdAccess); - return readBytes(getFD(fileDesc), bytes, off, len); + return readBytes(getFD(self, fdAccess), bytes, off, len); } /** @@ -412,21 +385,13 @@ public int readBytes(@JavaType(Object.class) StaticObject self, public int readBytes(@JavaType(Object.class) StaticObject self, FDAccess fdAccess, ByteBuffer buffer) { - StaticObject fileDesc = getFileDesc(self, fdAccess); - int fd = getFD(fileDesc); - Channel channel = Checks.ensureOpen(getChannel(fd), getContext()); - if (channel instanceof ReadableByteChannel readableByteChannel) { - try { - return readableByteChannel.read(buffer); - } catch (NonReadableChannelException e) { - throw Throw.throwNonReadable(context); - } catch (IOException e) { - throw Throw.throwIOException(e, context); - } - } else { + try { + return getReadableChannel(self, fdAccess).read(buffer); + } catch (NonReadableChannelException e) { throw Throw.throwNonReadable(context); + } catch (IOException e) { + throw Throw.throwIOException(e, context); } - } /** @@ -438,12 +403,7 @@ public int readBytes(@JavaType(Object.class) StaticObject self, public int readBytes(int fd, byte[] bytes, int off, int len) { - Channel channel = Checks.ensureOpen(getChannel(fd), getContext()); - if (channel instanceof ReadableByteChannel readableByteChannel) { - return readBytesImpl(readableByteChannel, bytes, off, len, context); - } else { - throw Throw.throwNonReadable(context); - } + return readBytesImpl(getReadableChannel(fd), bytes, off, len, context); } /** @@ -457,8 +417,7 @@ public int readBytes(int fd, @TruffleBoundary public boolean drain(@JavaType(Object.class) StaticObject self, FDAccess fdAccess) { - StaticObject fileDesc = getFileDesc(self, fdAccess); - return drain(getFD(fileDesc)); + return drain(getFD(self, fdAccess)); } /** @@ -469,22 +428,18 @@ public boolean drain(@JavaType(Object.class) StaticObject self, */ @TruffleBoundary public boolean drain(int fd) { - Channel channel = Checks.ensureOpen(getChannel(fd), getContext()); - if (channel instanceof ReadableByteChannel readableByteChannel) { - try { - ByteBuffer bytes = ByteBuffer.wrap(new byte[DRAIN_SEGMENT_COUNT]); - boolean discarded = false; - while ((readableByteChannel.read(bytes)) > 0) { - discarded = true; - } - return discarded; - } catch (NonReadableChannelException e) { - throw Throw.throwNonReadable(context); - } catch (IOException e) { - throw Throw.throwIOException(e, context); + try { + ByteBuffer bytes = ByteBuffer.wrap(new byte[DRAIN_SEGMENT_COUNT]); + boolean discarded = false; + ReadableByteChannel readableByteChannel = getReadableChannel(fd); + while ((readableByteChannel.read(bytes)) > 0) { + discarded = true; } - } else { + return discarded; + } catch (NonReadableChannelException e) { throw Throw.throwNonReadable(context); + } catch (IOException e) { + throw Throw.throwIOException(e, context); } } @@ -499,19 +454,9 @@ public boolean drain(int fd) { @TruffleBoundary public long position(@JavaType(Object.class) StaticObject self, FDAccess fdAccess) { - StaticObject fileDesc = getFileDesc(self, fdAccess); - return position(getFD(fileDesc)); + return position(getFD(self, fdAccess)); } - /** - * Updates the current position of the file-pointer associated with the given file descriptor - * holder. - * - * @param self The file descriptor holder. - * @param fdAccess How to get the file descriptor from the holder. - * @param newPos the new Position - */ - /** * Returns the current position the file associated with the given file descriptor holder is * pointing to. @@ -520,15 +465,11 @@ public long position(@JavaType(Object.class) StaticObject self, */ @TruffleBoundary public long position(int fd) { - Channel channel = Checks.ensureOpen(getChannel(fd), getContext()); - if (channel instanceof SeekableByteChannel seekableChannel) { - try { - return seekableChannel.position(); - } catch (IOException e) { - throw Throw.throwIOException(e, context); - } - } else { - return 0; // unknown + SeekableByteChannel seekableChannel = getSeekableChannel(fd); + try { + return seekableChannel.position(); + } catch (IOException e) { + throw Throw.throwIOException(e, context); } } @@ -558,15 +499,11 @@ public void seek(@JavaType(Object.class) StaticObject self, @TruffleBoundary public void seek(int fd, long pos) { - Channel channel = Checks.ensureOpen(getChannel(fd), getContext()); - if (channel instanceof SeekableByteChannel seekableChannel) { - try { - seekableChannel.position(pos); - } catch (IOException e) { - throw Throw.throwIOException(e, context); - } - } else { - Throw.throwNonSeekable(context); + SeekableByteChannel seekableChannel = getSeekableChannel(fd); + try { + seekableChannel.position(pos); + } catch (IOException e) { + throw Throw.throwIOException(e, context); } } @@ -597,6 +534,10 @@ private Channel getChannel(int fd) { return getFileChannel(fd); } + private int getFD(StaticObject self, FDAccess fdAccess) { + return getFD(getFileDesc(self, fdAccess)); + } + private int getFD(@JavaType(FileDescriptor.class) StaticObject fileDescriptor) { assert !StaticObject.isNull(fileDescriptor); return java_io_FileDescriptor_fd.getInt(fileDescriptor); @@ -855,6 +796,32 @@ private static int readBytesImpl(ReadableByteChannel readableChannel, byte[] b, } } + private ReadableByteChannel getReadableChannel(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess) { + return getReadableChannel(getFD(getFileDesc(self, fdAccess))); + } + + private ReadableByteChannel getReadableChannel(int fd) { + Channel channel = Checks.ensureOpen(getChannel(fd), getContext()); + if (channel instanceof ReadableByteChannel readableByteChannel) { + return readableByteChannel; + } + throw Throw.throwNonReadable(context); + } + + private WritableByteChannel getWritableChannel(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess) { + return getWritableChannel(getFD(getFileDesc(self, fdAccess))); + } + + private WritableByteChannel getWritableChannel(int fd) { + Channel channel = Checks.ensureOpen(getChannel(fd), getContext()); + if (channel instanceof WritableByteChannel writableByteChannel) { + return writableByteChannel; + } + throw Throw.throwNonWritable(context); + } + private static int writeBytesImpl(WritableByteChannel writableChannel, byte[] b, int off, int len, EspressoContext context) { try { @@ -877,6 +844,14 @@ private static long sizeImpl(SeekableByteChannel seekableChannel, EspressoContex } } + private SeekableByteChannel getSeekableChannel(int fd) { + Channel channel = Checks.ensureOpen(getChannel(fd), getContext()); + if (channel instanceof SeekableByteChannel seekableChannel) { + return seekableChannel; + } + throw Throw.throwNonSeekable(context); + } + private static int lookupSyncedValue(ObjectKlass klass, Symbol constant) { Field f = klass.lookupDeclaredField(constant, Types._int); EspressoError.guarantee(f != null, "Failed to sync " + klass.getExternalName() + " constants"); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsState.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsState.java index ae550079280e..15af2b9c3941 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsState.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsState.java @@ -22,19 +22,31 @@ */ package com.oracle.truffle.espresso.libs; +import java.io.FileDescriptor; import java.util.zip.Inflater; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.TruffleLogger; import com.oracle.truffle.espresso.EspressoLanguage; +import com.oracle.truffle.espresso.io.FDAccess; +import com.oracle.truffle.espresso.io.TruffleIO; import com.oracle.truffle.espresso.jni.StrongHandles; import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.EspressoException; +import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; +import com.oracle.truffle.espresso.substitutions.JavaType; public class LibsState { private static final TruffleLogger logger = TruffleLogger.getLogger(EspressoLanguage.ID, LibsState.class); + public static final FDAccess FD = new FDAccess() { + @Override + public @JavaType(FileDescriptor.class) StaticObject get(@JavaType(Object.class) StaticObject objectWithFD, TruffleIO io) { + return objectWithFD; + } + }; + private final StrongHandles handle2Inflater = new StrongHandles<>(); public long handlifyInflater(Inflater i) { From 2669d987217ad6f28ce6384c3c971ceabf420998 Mon Sep 17 00:00:00 2001 From: Elia Trachsel Date: Mon, 11 Aug 2025 15:49:37 +0200 Subject: [PATCH 05/26] fixup! Substitution for the TruffleFileSystem: --- .../com/oracle/truffle/espresso/runtime/EspressoContext.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java index e80fadc60e75..0bd0ebfd4d19 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java @@ -581,7 +581,9 @@ private void spawnVM() throws ContextPatchingException { meta.java_lang_OutOfMemoryError.lookupDeclaredMethod(Names._init_, Signatures._void_String).invokeDirectSpecial(outOfMemoryErrorInstance, meta.toGuestString("VM OutOfMemory")); meta.postSystemInit(); - truffleIO.postSystemInit(); + if (language.useEspressoLibs()) { + truffleIO.postSystemInit(); + } // class redefinition will be enabled if debug mode or if any redefine or retransform // capable java agent is present From 5f1ca1bf0fb20176f2f53eddc2516d5600d3e649 Mon Sep 17 00:00:00 2001 From: Elia Trachsel Date: Tue, 12 Aug 2025 09:20:56 +0200 Subject: [PATCH 06/26] Networking Support for EspressoLibs We added an option to disable networking on the user side. Then if we reach networking classes we always check if its enabled from the user and the Truffle side. Generally we use the host system to implement all networking functionalities by associating a fd with the appropriate channel (eg ServerSocketChannel) in TruffleIO. Then in the native methods we can use the provided fd to retrieve the corresponding host channel to implement the semantics. --- .../truffle/espresso/EspressoLanguage.java | 10 + .../truffle/espresso/EspressoOptions.java | 6 + .../espresso/descriptors/EspressoSymbols.java | 53 ++- .../ffi/EspressoLibsNativeAccess.java | 13 +- .../com/oracle/truffle/espresso/io/Throw.java | 36 ++ .../oracle/truffle/espresso/io/TruffleIO.java | 382 +++++++++++++++++- .../espresso/libs/InformationLeak.java | 114 ++++++ .../truffle/espresso/libs/LibsMeta.java | 73 ++++ .../truffle/espresso/libs/LibsState.java | 145 ++++++- .../truffle/espresso/libs/libnet/LibNet.java | 5 +- .../impl/Target_java_net_Inet4Address.java | 37 ++ .../impl/Target_java_net_Inet6Address.java | 37 ++ .../Target_java_net_Inet6AddressImpl.java | 105 +++++ .../impl/Target_java_net_InetAddress.java | 50 +++ .../Target_java_net_NetworkInterface.java | 317 +++++++++++++++ .../libnio/impl/Target_sun_nio_ch_Net.java | 333 +++++++++++++++ .../espresso/runtime/EspressoContext.java | 9 +- 17 files changed, 1702 insertions(+), 23 deletions(-) create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/InformationLeak.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnet/impl/Target_java_net_Inet4Address.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnet/impl/Target_java_net_Inet6Address.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnet/impl/Target_java_net_Inet6AddressImpl.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnet/impl/Target_java_net_InetAddress.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnet/impl/Target_java_net_NetworkInterface.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_Net.java diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoLanguage.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoLanguage.java index d7edcdc62cfb..be42fb0298e2 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoLanguage.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoLanguage.java @@ -148,6 +148,7 @@ public final class EspressoLanguage extends TruffleLanguage imp @CompilationFinal private boolean eagerFrameAnalysis; @CompilationFinal private boolean internalJvmciEnabled; @CompilationFinal private boolean useEspressoLibs; + @CompilationFinal private boolean enableNetworking; @CompilationFinal private boolean continuum; @CompilationFinal private String nativeBackendId; @CompilationFinal private boolean useTRegex; @@ -265,6 +266,11 @@ private void initializeOptions(final TruffleLanguage.Env env) { case graal -> new GraalGuestFieldOffsetStrategy(); }; this.useEspressoLibs = env.getOptions().get(EspressoOptions.UseEspressoLibs); + boolean userEnableNetworking = env.getOptions().get((EspressoOptions.enableNetworking)); + if (userEnableNetworking && !env.isSocketIOAllowed()) { + throw EspressoError.shouldNotReachHere("Socket IO is not allowed, but Espresso networking is enabled."); + } + this.enableNetworking = userEnableNetworking; this.nativeBackendId = setNativeBackendId(env); assert guestFieldOffsetStrategy.name().equals(strategy.name()); } @@ -630,6 +636,10 @@ public String nativeBackendId() { return nativeBackendId; } + public boolean enableNetworking() { + return enableNetworking; + } + public boolean isContinuumEnabled() { return continuum; } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoOptions.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoOptions.java index 22cc6fc3b32e..c43d62e3f95e 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoOptions.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoOptions.java @@ -633,6 +633,12 @@ public Long apply(String size) { usageSyntax = "true|false") // public static final OptionKey UseEspressoLibs = new OptionKey<>(false); + @Option(help = "Enables/Disables the use of Networking until Truffle implements something (GR-23755)", // + category = OptionCategory.EXPERT, // + stability = OptionStability.EXPERIMENTAL, // + usageSyntax = "true|false") // + public static final OptionKey enableNetworking = new OptionKey<>(true); + @Option(help = "Enables the signal API (sun.misc.Signal or jdk.internal.misc.Signal).", // category = OptionCategory.EXPERT, // stability = OptionStability.EXPERIMENTAL, // diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java index c808d4f6db6e..de0248fdc5f8 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java @@ -131,6 +131,7 @@ public static void ensureInitialized() { public static final Symbol java_nio_file_AtomicMoveNotSupportedException = SYMBOLS.putType("Ljava/nio/file/AtomicMoveNotSupportedException;"); public static final Symbol java_nio_file_AccessDeniedException = SYMBOLS.putType("Ljava/nio/file/AccessDeniedException;"); public static final Symbol java_nio_file_NoSuchFileException = SYMBOLS.putType("Ljava/nio/file/NoSuchFileException;"); + public static final Symbol java_net_SocketException = SYMBOLS.putType("Ljava/net/SocketException;"); public static final Symbol java_nio_file_InvalidPathException = SYMBOLS.putType("Ljava/nio/file/InvalidPathException;"); public static final Symbol java_nio_file_NotDirectoryException = SYMBOLS.putType("Ljava/nio/file/NotDirectoryException;"); @@ -162,10 +163,25 @@ public static void ensureInitialized() { public static final Symbol java_lang_module_ModuleDescriptor = SYMBOLS.putType("Ljava/lang/module/ModuleDescriptor;"); // Espresso Libs + // libzip public static final Symbol java_util_zip_CRC32 = SYMBOLS.putType("Ljava/util/zip/CRC32;"); public static final Symbol java_util_zip_Inflater = SYMBOLS.putType("Ljava/util/zip/Inflater;"); public static final Symbol java_util_zip_DataFormatException = SYMBOLS.putType("Ljava/util/zip/DataFormatException;"); - + // libnet + public static final Symbol java_net_NetworkInterface = SYMBOLS.putType("Ljava/net/NetworkInterface;"); + public static final Symbol java_net_NetworkInterface_array = SYMBOLS.putType("[Ljava/net/NetworkInterface;"); + public static final Symbol java_net_InetSocketAddress = SYMBOLS.putType("Ljava/net/InetSocketAddress;"); + public static final Symbol java_net_InetAddress = SYMBOLS.putType("Ljava/net/InetAddress;"); + public static final Symbol java_net_InetAddress$InetAddressHolder = SYMBOLS.putType("Ljava/net/InetAddress$InetAddressHolder;"); + public static final Symbol java_net_InterfaceAddress = SYMBOLS.putType("Ljava/net/InterfaceAddress;"); + public static final Symbol java_net_InterfaceAddress_array = SYMBOLS.putType("[Ljava/net/InterfaceAddress;"); + public static final Symbol java_net_Inet4Address = SYMBOLS.putType("Ljava/net/Inet4Address;"); + public static final Symbol java_net_Inet6Address = SYMBOLS.putType("Ljava/net/Inet6Address;"); + public static final Symbol java_net_Inet6Address$Inet6AddressHolder = SYMBOLS.putType("Ljava/net/Inet6Address$Inet6AddressHolder;"); + public static final Symbol java_net_InetAddress_array = SYMBOLS.putType("[Ljava/net/InetAddress;"); + public static final Symbol sun_net_ConnectionResetException = SYMBOLS.putType("Lsun/net/ConnectionResetException;"); + public static final Symbol java_net_UnknownHostException = SYMBOLS.putType("Ljava/net/UnknownHostException;"); + public static final Symbol sun_nio_ch_IOStatus = SYMBOLS.putType("Lsun/nio/ch/IOStatus;"); // URL class loader public static final Symbol java_net_URLClassLoader = SYMBOLS.putType("Ljava/net/URLClassLoader;"); public static final Symbol java_net_URL = SYMBOLS.putType("Ljava/net/URL;"); @@ -773,6 +789,23 @@ public static class Names { public static final Symbol HIDDEN_CRC32 = SYMBOLS.putName("0HIDDEN_CRC32"); public static final Symbol inputConsumed = SYMBOLS.putName("inputConsumed"); public static final Symbol outputConsumed = SYMBOLS.putName("outputConsumed"); + // java.net + public static final Symbol displayName = SYMBOLS.putName("displayName"); + public static final Symbol virtual = SYMBOLS.putName("virtual"); + public static final Symbol bindings = SYMBOLS.putName("bindings"); + public static final Symbol childs = SYMBOLS.putName("childs"); + public static final Symbol scope_ifname = SYMBOLS.putName("scope_ifname"); + public static final Symbol holder6 = SYMBOLS.putName("holder6"); + public static final Symbol ipaddress = SYMBOLS.putName("ipaddress"); + public static final Symbol maskLength = SYMBOLS.putName("maskLength"); + public static final Symbol broadcast = SYMBOLS.putName("broadcast"); + // sun.nio.ch.IOStatus + public static final Symbol EOF = SYMBOLS.putName("EOF"); + public static final Symbol UNAVAILABLE = SYMBOLS.putName("UNAVAILABLE"); + public static final Symbol INTERRUPTED = SYMBOLS.putName("INTERRUPTED"); + public static final Symbol UNSUPPORTED = SYMBOLS.putName("UNSUPPORTED"); + public static final Symbol THROWN = SYMBOLS.putName("THROWN"); + public static final Symbol UNSUPPORTED_CASE = SYMBOLS.putName("UNSUPPORTED_CASE"); // java.lang.invoke.* // CallSite public static final Symbol target = SYMBOLS.putName("target"); @@ -1453,6 +1486,24 @@ public static class Signatures { Types._int, Types.java_lang_Object); + public static final Symbol java_net_NetworkInterface_init_signature = SYMBOLS.putSignature(Types._void, + /* name */ Types.java_lang_String, + /* index */ Types._int, + /* addrs */ Types.java_net_InetAddress_array); + + public static final Symbol java_net_Inet4Address_init_signature = SYMBOLS.putSignature(Types._void, + /* hostName */ Types.java_lang_String, + /* address */ Types._byte_array); + + public static final Symbol java_net_Inet6Address_init_signature = SYMBOLS.putSignature(Types._void, + /* hostName */ Types.java_lang_String, + /* address */ Types._byte_array, + /* scopeId */ Types._int); + + public static final Symbol java_net_InetSocketAddress_init_signature = SYMBOLS.putSignature(Types._void, + /* addr */ Types.java_net_InetAddress, + /* port */ Types._int); + public static void ensureInitialized() { assert _void == ParserSymbols.ParserSignatures._void; } 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 66e7451cd329..5618cc90ec4c 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 @@ -23,6 +23,7 @@ package com.oracle.truffle.espresso.ffi; import java.nio.file.Path; +import java.util.Arrays; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.TruffleLogger; @@ -75,7 +76,10 @@ public EspressoLibsNativeAccess(EspressoContext ctx, NativeAccess delegate) { } // Failed to find library in known libs: // try to find an actual library (can be null) - getLogger().fine(() -> "Could not find espresso lib '" + libname + "', trying delegate Native Access..."); + String[] knownLibs = {"libnespresso.so", "libjvm.so", "libverify.so", "libjimage.so"}; + if (!Arrays.asList(knownLibs).contains(libname.toString())) { + getLogger().warning(() -> "Could not find espresso lib '" + libname + "', trying delegate Native Access..."); + } return delegate.loadLibrary(libraryPath); } @@ -115,8 +119,11 @@ public void unloadLibrary(@Pointer TruffleObject library) { getLogger().fine(() -> "Failed to locate symbol '" + symbolName + "' in espresso lib " + lib.name()); } else { // Delegate library - getLogger().fine(() -> "Espresso libs delegating for: " + symbolName); - return delegate.lookupSymbol(library, symbolName); + TruffleObject ret = delegate.lookupSymbol(library, symbolName); + if (ret != null) { + getLogger().fine(() -> "Found: " + symbolName + " through delegate library"); + } + return ret; } return null; } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/Throw.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/Throw.java index 7607775c8777..6046c9d143d2 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/Throw.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/Throw.java @@ -23,6 +23,8 @@ package com.oracle.truffle.espresso.io; import java.io.IOException; +import java.net.SocketException; +import java.net.UnknownHostException; import java.nio.channels.AsynchronousCloseException; import java.nio.channels.ClosedByInterruptException; import java.nio.channels.ClosedChannelException; @@ -60,6 +62,14 @@ public static EspressoException throwIOException(String message, EspressoContext throw context.getMeta().throwExceptionWithMessage(context.getTruffleIO().java_io_IOException, message); } + public static EspressoException throwSocketException(String message, EspressoContext context) { + throw context.getMeta().throwExceptionWithMessage(context.getTruffleIO().java_net_SocketException, message); + } + + public static EspressoException throwSocketException(SocketException e, EspressoContext context) { + throw throwSocketException(getMessageBoundary(e), context); + } + public static EspressoException throwIOException(IOException e, EspressoContext context) { Class exceptionClass = e.getClass(); String message = getMessageBoundary(e); @@ -103,6 +113,18 @@ public static EspressoException throwIOException(IOException e, EspressoContext throw throwNotDirectoryException(message, context); } + if (exceptionClass == UnknownHostException.class) { + throw throwUnknownHostException(message, context); + } + + if (exceptionClass == SocketException.class) { + throw throwSocketException(message, context); + } + + if (isConnectionResetException(e)) { + throw throwConnectionResetException(message, context); + } + if (exceptionClass != IOException.class) { context.getLogger().warning(() -> "Not exact translation of IOException: " + exceptionClass); } @@ -121,6 +143,10 @@ public static EspressoException throwClosedChannelException(String message, Espr throw context.getMeta().throwExceptionWithMessage(context.getTruffleIO().java_nio_channels_ClosedChannelException, message); } + private static boolean isConnectionResetException(IOException e) { + return e.getClass() == SocketException.class && (getMessageBoundary(e).equals("Connection reset")); + } + public static EspressoException throwNonReadable(EspressoContext context) { throw throwIOException("non-readable", context); } @@ -186,4 +212,14 @@ public static EspressoException throwNotLinkException(String message, EspressoCo Meta meta = context.getMeta(); throw meta.throwExceptionWithMessage(meta.java_nio_file_NotLinkException, message); } + + public static EspressoException throwConnectionResetException(String message, EspressoContext context) { + Meta meta = context.getMeta(); + return meta.throwExceptionWithMessage(context.getTruffleIO().sun_net_ConnectionResetException, message); + } + + public static EspressoException throwUnknownHostException(String message, EspressoContext context) { + Meta meta = context.getMeta(); + return meta.throwExceptionWithMessage(context.getTruffleIO().java_net_UnknownHostException, message); + } } 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 39c6147b6fe9..aa4c2e8e3d82 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 @@ -30,18 +30,31 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.RandomAccessFile; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.SocketOption; +import java.net.StandardProtocolFamily; +import java.net.StandardSocketOptions; import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousCloseException; import java.nio.channels.Channel; import java.nio.channels.Channels; +import java.nio.channels.ClosedByInterruptException; +import java.nio.channels.DatagramChannel; +import java.nio.channels.NetworkChannel; import java.nio.channels.NonReadableChannelException; import java.nio.channels.NonWritableChannelException; import java.nio.channels.ReadableByteChannel; import java.nio.channels.SeekableByteChannel; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; import java.nio.channels.WritableByteChannel; import java.nio.file.OpenOption; import java.nio.file.StandardOpenOption; import java.nio.file.attribute.FileAttribute; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; @@ -62,11 +75,13 @@ import com.oracle.truffle.espresso.impl.Field; import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.impl.ObjectKlass; +import com.oracle.truffle.espresso.libs.LibsState; 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.OS; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; +import com.oracle.truffle.espresso.substitutions.JavaSubstitution; import com.oracle.truffle.espresso.substitutions.JavaType; /** @@ -88,10 +103,13 @@ public final class TruffleIO implements ContextAccess { // Checkstyle: stop field name check public final ObjectKlass java_io_IOException; public final ObjectKlass java_nio_file_NoSuchFileException; + public final ObjectKlass java_net_SocketException; public final ObjectKlass java_io_FileNotFoundException; public final ObjectKlass java_nio_channels_ClosedByInterruptException; public final ObjectKlass java_nio_channels_AsynchronousCloseException; public final ObjectKlass java_nio_channels_ClosedChannelException; + public final ObjectKlass sun_net_ConnectionResetException; + public final ObjectKlass java_net_UnknownHostException; public final ObjectKlass java_io_FileDescriptor; public final Field java_io_FileDescriptor_fd; public final Field java_io_FileDescriptor_append; @@ -122,6 +140,9 @@ public final class TruffleIO implements ContextAccess { public final ObjectKlass sun_nio_fs_FileAttributeParser; @CompilationFinal public FileAttributeParser_Sync fileAttributeParserSync; + public final ObjectKlass sun_nio_ch_IOStatus; + public final IOStatus_Sync ioStatusSync; + public final ObjectKlass java_io_FileSystem; public final FileSystem_Sync fileSystemSync; // Checkstyle: resume field name check @@ -215,6 +236,288 @@ public int open(@JavaType(Object.class) StaticObject self, return fd; } + /** + * Temporary Method to open a Socket with the given parameter and associate it with a generated + * fd. It will be replaced with a TruffleAPI as soon as one exists. + * + * @param preferIPv6 whether to prefer IPv6 over IPv4 + * @param stream TCP or UDP + * @param reuse allows binding to an address even if in TIME_WAIT state + * + * @return The file descriptor associated with the file. + */ + @TruffleBoundary + public int openSocket(boolean preferIPv6, boolean stream, boolean reuse) { + context.getInformationLeak().checkNetworkEnabled(); + // opening the channel + java.net.ProtocolFamily family = preferIPv6 ? StandardProtocolFamily.INET6 : StandardProtocolFamily.INET; + ChannelWrapper channelWrapper; + try { + if (stream && !reuse) { + // this implies that we are opening a TCP Client Channel + // according to sun.nio.ch.Net.socket(java.net.ProtocolFamily,boolean) + SocketChannel channel = SocketChannel.open(family); + channel.setOption(StandardSocketOptions.SO_REUSEADDR, reuse); + channelWrapper = new ChannelWrapper(channel, 1); + } else if (stream) { + // opens a TCP Server Channel + ServerSocketChannel channel = ServerSocketChannel.open(family); + channel.setOption(StandardSocketOptions.SO_REUSEADDR, reuse); + channelWrapper = new TCPChannelWrapper(channel, 1); + } else { + // opens a UDP Channel + DatagramChannel channel = DatagramChannel.open(StandardProtocolFamily.INET); + channel.setOption(StandardSocketOptions.SO_REUSEADDR, reuse); + channelWrapper = new ChannelWrapper(channel, 1); + } + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } + return createFDforChannel(channelWrapper); + } + + @TruffleBoundary + public int available(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess) { + try { + return Channels.newInputStream(getReadableChannel(self, fdAccess)).available(); + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } + } + + @TruffleBoundary + public void bind(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess, boolean preferIPv6, boolean useExclBind, @JavaType(InetAddress.class) StaticObject addr, + int port, LibsState libsState) { + ChannelWrapper channelWrapper = files.getOrDefault(getFD(self, fdAccess), null); + Objects.requireNonNull(channelWrapper); + Channel channel = channelWrapper.channel; + + InetAddress inetAddress = libsState.net.fromGuestInetAddress(addr); + + if (channel instanceof DatagramChannel datagramChannel) { + this.bindUDP(datagramChannel, inetAddress, preferIPv6, useExclBind, port); + } else if (channelWrapper instanceof TCPChannelWrapper tcpChannelWrapper) { + /* + * We shouldn't call bind directly on the ServerSocketChannel since we lack the backlog. + * parameter which will be provided by the listen method. Thus, we cache the arguments + * but wait with the bind. + */ + tcpChannelWrapper.setTCPBindInformation(inetAddress, preferIPv6, useExclBind, port); + } else { + // the Server-fd isn't associated with a TCP-Channel for a TCP protocol method! + throw JavaSubstitution.shouldNotReachHere(); + } + } + + @TruffleBoundary + public void bindUDP(DatagramChannel datagramChannel, InetAddress inetAddress, boolean preferIPv6, boolean useExclBind, int port) { + this.initSocketOptions(datagramChannel, preferIPv6, useExclBind); + try { + datagramChannel.bind(new InetSocketAddress(inetAddress, port)); + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } + } + + @TruffleBoundary + public int accept(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess, @JavaType(FileDescriptor.class) StaticObject newfd, SocketAddress[] ret, + LibsState libsState) { + Channel channel = Checks.ensureOpen(getChannel(getFD(self, fdAccess)), getContext()); + if (channel instanceof ServerSocketChannel serverSocketChannel) { + try { + // accept the connection + SocketChannel clientSocket = serverSocketChannel.accept(); + if (clientSocket == null) { + return this.ioStatusSync.UNAVAILABLE; + } + // register the channel with a fd + int newfdVal = createFDforChannel(new ChannelWrapper(clientSocket, 1)); + // set the value of the fd + libsState.net.setFDVal(newfd, newfdVal); + // return the remoteAddress + ret[0] = clientSocket.getRemoteAddress(); + return 1; + } catch (AsynchronousCloseException e) { + return ioStatusSync.UNAVAILABLE; + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } + } else { + // the Server-fd isn't associated with a TCP-Channel for a TCP protocol method! + throw JavaSubstitution.shouldNotReachHere(); + } + } + + /** + * Calls finishConnect on the underlying SocketChannel. + */ + @TruffleBoundary + public boolean finishConnect(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess) { + try { + return getSocketChannel(self, fdAccess).finishConnect(); + } catch (IOException e) { + return false; + } + } + + public void setSocketOption(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess, SocketOption name, T value) { + try { + getNetworkChannel(self, fdAccess).setOption(name, value); + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } + } + + public boolean connect(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess, SocketAddress remote) { + try { + return getSocketChannel(self, fdAccess).connect(remote); + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } + } + + public void shutdownSocketChannel(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess, int how) { + try { + SocketChannel socketChannel = getSocketChannel(self, fdAccess); + if (how == 2 || how == 0) { + socketChannel.shutdownInput(); + } + if (how == 1 || how == 2) { + socketChannel.shutdownOutput(); + } + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } + } + + public T getSocketOption(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess, SocketOption name) { + try { + return getNetworkChannel(self, fdAccess).getOption(name); + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } + } + + private int createFDforChannel(ChannelWrapper channel) { + synchronized (files) { + int fd = nextFreeFd(); + if (fd < 0) { + throw Throw.throwFileNotFoundException("Opened file limit reached.", context); + } + files.put(fd, channel); + return fd; + } + } + + @TruffleBoundary + public void listenTCP(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess, int backlog) { + int fd = getFD(getFileDesc(self, fdAccess)); + ChannelWrapper channelWrapper = files.getOrDefault(fd, null); + Objects.requireNonNull(channelWrapper); + if (channelWrapper instanceof TCPChannelWrapper tcpWrapper) { + ServerSocketChannel channel = (ServerSocketChannel) channelWrapper.channel; + initSocketOptions(channel, tcpWrapper.preferIPv6, tcpWrapper.useExclBind); + try { + channel.bind(new InetSocketAddress(tcpWrapper.inetAddress, tcpWrapper.port), backlog); + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } + } else { + throw JavaSubstitution.shouldNotReachHere(); + } + } + + @TruffleBoundary + public @JavaType StaticObject getLocalAddress(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess) { + + try { + int fd = getFD(self, fdAccess); + NetworkChannel networkChannel = getNetworkChannel(fd); + InetSocketAddress socketAddress = (InetSocketAddress) networkChannel.getLocalAddress(); + InetAddress inetAddress = null; + if (socketAddress != null) { + inetAddress = socketAddress.getAddress(); + } else { + /* + * The host socket is bound once listen is called. On the other hand, the guest + * socket is bound by the call to bind (which proceeds the listen call. Thus, we + * need to check if we have cached the bind information. + */ + TCPChannelWrapper tcpSocket = socketIsBound(fd); + if (tcpSocket != null) { + inetAddress = tcpSocket.inetAddress; + } else { + throw Throw.throwIOException("Unbound Socket", context); + } + } + return context.getLibsState().net.convertInetAddr(inetAddress); + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } + } + + @TruffleBoundary + public int getPort(@JavaType(Object.class) StaticObject self, FDAccess fdAccess) { + try { + int fd = getFD(self, fdAccess); + InetSocketAddress socketAddress = (InetSocketAddress) getNetworkChannel(fd).getLocalAddress(); + if (socketAddress != null) { + return socketAddress.getPort(); + } + /* + * The host socket is bound once listen is called. On the other hand, the guest socket + * is bound by the call to bind (which proceeds the listen call. Thus, we need to check + * if we have cached the bind information. + */ + TCPChannelWrapper tcpSocket = socketIsBound(fd); + if (tcpSocket != null) { + return tcpSocket.port; + } + throw Throw.throwIOException("Unbound Socket", context); + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } + } + + private TCPChannelWrapper socketIsBound(int fd) { + if (files.getOrDefault(fd, null) instanceof TCPChannelWrapper tcpChannelWrapper) { + if (tcpChannelWrapper.inetAddress != null) { + return tcpChannelWrapper; + } + } + return null; + } + + private void initSocketOptions(NetworkChannel networkChannel, boolean preferIPv6, boolean useExclBind) { + try { + if (preferIPv6) { + // might be overkill. We could just let the system figure out what it wants to use. + System.setProperty("java.net.preferIPv6Addresses", "true"); + System.setProperty("java.net.preferIPv6Addresses", "false"); + } else { + System.setProperty("java.net.preferIPv6Addresses", "false"); + System.setProperty("java.net.preferIPv6Addresses", "true"); + } + } catch (SecurityException e) { + // ignore preferIPv6 + } + try { + networkChannel.setOption(StandardSocketOptions.SO_REUSEADDR, !useExclBind); + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } + } + /** * Opens a file and associates it with the given file descriptor holder. * @@ -291,6 +594,8 @@ public int writeBytes(@JavaType(Object.class) StaticObject self, ByteBuffer bytes) { try { return getWritableChannel(self, fdAccess).write(bytes); + } catch (ClosedByInterruptException e) { + return ioStatusSync.UNAVAILABLE; } catch (NonWritableChannelException e) { throw Throw.throwNonWritable(context); } catch (IOException e) { @@ -387,6 +692,8 @@ public int readBytes(@JavaType(Object.class) StaticObject self, ByteBuffer buffer) { try { return getReadableChannel(self, fdAccess).read(buffer); + } catch (ClosedByInterruptException e) { + return ioStatusSync.UNAVAILABLE; } catch (NonReadableChannelException e) { throw Throw.throwNonReadable(context); } catch (IOException e) { @@ -486,8 +793,7 @@ public long position(int fd) { public void seek(@JavaType(Object.class) StaticObject self, FDAccess fdAccess, long pos) { - StaticObject fileDesc = getFileDesc(self, fdAccess); - seek(getFD(fileDesc), pos); + seek(getFD(self, fdAccess), pos); } /** @@ -601,6 +907,24 @@ void withPath(String newPath) { } } + private static class TCPChannelWrapper extends ChannelWrapper { + InetAddress inetAddress; + boolean preferIPv6; + boolean useExclBind; + int port; + + TCPChannelWrapper(Channel channel, int cnt) { + super(channel, cnt, null); + } + + void setTCPBindInformation(InetAddress inetAddress, boolean preferIPv6, boolean useExclBind, int port) { + this.inetAddress = inetAddress; + this.preferIPv6 = preferIPv6; + this.useExclBind = useExclBind; + this.port = port; + } + } + public TruffleIO(EspressoContext context) { this.context = context; @@ -645,6 +969,9 @@ public TruffleIO(EspressoContext context) { java_nio_channels_AsynchronousCloseException = meta.knownKlass(Types.java_nio_channels_AsynchronousCloseException); java_nio_channels_ClosedChannelException = meta.knownKlass(Types.java_nio_channels_ClosedChannelException); java_nio_file_NoSuchFileException = meta.knownKlass(Types.java_nio_file_NoSuchFileException); + java_net_SocketException = meta.knownKlass(Types.java_net_SocketException); + sun_net_ConnectionResetException = meta.knownKlass(EspressoSymbols.Types.sun_net_ConnectionResetException); + java_net_UnknownHostException = meta.knownKlass(EspressoSymbols.Types.java_net_UnknownHostException); java_io_File = meta.knownKlass(Types.java_io_File); java_io_File_path = java_io_File.requireDeclaredField(Names.path, Types.java_lang_String); @@ -660,6 +987,9 @@ public TruffleIO(EspressoContext context) { sun_nio_fs_FileAttributeParser = meta.knownKlass(EspressoSymbols.Types.sun_nio_fs_FileAttributeParser); + sun_nio_ch_IOStatus = meta.knownKlass(EspressoSymbols.Types.sun_nio_ch_IOStatus); + ioStatusSync = new IOStatus_Sync(this); + sun_nio_fs_TrufflePath = meta.knownKlass(Types.sun_nio_fs_TrufflePath); sun_nio_fs_TrufflePath_HIDDEN_TRUFFLE_FILE = sun_nio_fs_TrufflePath.requireHiddenField(Names.HIDDEN_TRUFFLE_FILE); @@ -771,7 +1101,7 @@ private static int readSingleImpl(ReadableByteChannel readableChannel, EspressoC if (bytesRead == 1) { return b[0] & 0xFF; } else { - return -1; // EOF + return context.getTruffleIO().ioStatusSync.EOF; // EOF } } catch (NonReadableChannelException e) { throw Throw.throwNonReadable(context); @@ -809,6 +1139,34 @@ private ReadableByteChannel getReadableChannel(int fd) { throw Throw.throwNonReadable(context); } + private SocketChannel getSocketChannel(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess) { + return getSocketChannel(getFD(getFileDesc(self, fdAccess))); + } + + private SocketChannel getSocketChannel(int fd) { + Channel channel = Checks.ensureOpen(getChannel(fd), getContext()); + if (channel instanceof SocketChannel socketChannel) { + return socketChannel; + } + // SocketChannels are backed by the host, thus it would be very suspicious if we reach here. + throw JavaSubstitution.shouldNotReachHere(); + } + + private NetworkChannel getNetworkChannel(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess) { + return getNetworkChannel(getFD(getFileDesc(self, fdAccess))); + } + + private NetworkChannel getNetworkChannel(int fd) { + Channel channel = Checks.ensureOpen(getChannel(fd), getContext()); + if (channel instanceof NetworkChannel networkChannel) { + return networkChannel; + } + // NetworkChannel are backed by the host, thus it would be very suspicious if we reach here. + throw JavaSubstitution.shouldNotReachHere(); + } + private WritableByteChannel getWritableChannel(@JavaType(Object.class) StaticObject self, FDAccess fdAccess) { return getWritableChannel(getFD(getFileDesc(self, fdAccess))); @@ -920,5 +1278,23 @@ public FileAttributeParser_Sync(TruffleIO io) { this.OTHERS_EXECUTE_VALUE = lookupSyncedValue(io.sun_nio_fs_FileAttributeParser, Names.OTHERS_EXECUTE_VALUE); } } + + public static final class IOStatus_Sync { + public final int EOF; + public final int UNAVAILABLE; + public final int INTERRUPTED; + public final int UNSUPPORTED; + public final int THROWN; + public final int UNSUPPORTED_CASE; + + public IOStatus_Sync(TruffleIO io) { + this.EOF = lookupSyncedValue(io.sun_nio_ch_IOStatus, Names.EOF); + this.UNAVAILABLE = lookupSyncedValue(io.sun_nio_ch_IOStatus, Names.UNAVAILABLE); + this.INTERRUPTED = lookupSyncedValue(io.sun_nio_ch_IOStatus, Names.INTERRUPTED); + this.UNSUPPORTED = lookupSyncedValue(io.sun_nio_ch_IOStatus, Names.UNSUPPORTED); + this.THROWN = lookupSyncedValue(io.sun_nio_ch_IOStatus, Names.THROWN); + this.UNSUPPORTED_CASE = lookupSyncedValue(io.sun_nio_ch_IOStatus, Names.UNSUPPORTED_CASE); + } + } // Checkstyle: resume field name check } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/InformationLeak.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/InformationLeak.java new file mode 100644 index 000000000000..2752af4b80df --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/InformationLeak.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs; + +import java.io.IOException; +import java.net.BindException; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetSocketAddress; +import java.net.NetworkInterface; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.net.StandardSocketOptions; +import java.nio.channels.ServerSocketChannel; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.espresso.io.Throw; +import com.oracle.truffle.espresso.runtime.EspressoContext; + +/** + * In the context of the EspressoLibs project, this class is designed to aggregate methods and + * fields that potentially leak information about the host system. Depending on the context, leaking + * such information might not be preferable due to security or privacy concerns. + */ +public class InformationLeak { + private final EspressoContext context; + + public InformationLeak(EspressoContext ctx) { + this.context = ctx; + } + + public void checkNetworkEnabled() { + if (!context.getLanguage().enableNetworking()) { + throw Throw.throwIllegalStateException("You are accessing deep LibNet classes even though networking is disabled", context); + } + } + + public boolean isIPv6Available0() { + this.checkNetworkEnabled(); + try (Socket s = new Socket()) { + s.bind(new InetSocketAddress(Inet6Address.getByName("::"), 0)); + return true; + } catch (IOException e) { + return false; + } + } + + public boolean isIPv4Available() { + this.checkNetworkEnabled(); + try (Socket s = new Socket()) { + s.bind(new InetSocketAddress(Inet4Address.getByName("0.0.0.0"), 0)); + return true; + } catch (IOException e) { + return false; + } + } + + @TruffleBoundary + public boolean isReusePortAvailable0() { + this.checkNetworkEnabled(); + try (ServerSocketChannel channel = ServerSocketChannel.open()) { + return channel.supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT); + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } + } + + @TruffleBoundary + public int isExclusvieBindAvailable() { + try (ServerSocket socket1 = new ServerSocket()) { + socket1.setReuseAddress(false); + socket1.bind(new InetSocketAddress(8080)); + + try (ServerSocket socket2 = new ServerSocket()) { + socket2.setReuseAddress(false); + socket2.bind(new InetSocketAddress(8080)); // Should fail if exclusive bind works + return -1; + } catch (BindException e) { + return 1; + } + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } + } + + public byte[] getMacAddress(NetworkInterface netIF) { + try { + return netIF.getHardwareAddress(); + } catch (SocketException e) { + throw Throw.throwSocketException(e, context); + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsMeta.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsMeta.java index fcd4a3975530..90ab0550fccd 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsMeta.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsMeta.java @@ -29,6 +29,7 @@ import com.oracle.truffle.espresso.descriptors.EspressoSymbols; import com.oracle.truffle.espresso.impl.ContextAccess; import com.oracle.truffle.espresso.impl.Field; +import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.impl.ObjectKlass; import com.oracle.truffle.espresso.meta.DiffVersionLoadHelper; import com.oracle.truffle.espresso.meta.Meta; @@ -76,4 +77,76 @@ public ObjectKlass knownKlass(Symbol type) { private DiffVersionLoadHelper diff() { return new DiffVersionLoadHelper(meta); } + + public final class LibNetMeta { + // Checkstyle: stop field name check + public final ObjectKlass java_net_InetAddress; + public final Field java_net_InetAddress_holder; + public final ObjectKlass java_net_InetAddress$InetAddressHolder; + public final Field java_net_InetAddress$InetAddressHolder_address; + + public final ObjectKlass java_net_InterfaceAddress; + public final Method java_net_InterfaceAddress_init; + public final Field java_net_InterfaceAddress_address; + public final Field java_net_InterfaceAddress_broadcast; + public final Field java_net_InterfaceAddress_maskLength; + + public final ObjectKlass java_net_Inet4Address; + public final Method java_net_Inet4Address_init; + + public final ObjectKlass java_net_Inet6Address; + public final Method java_net_Inet6Address_init; + public final Field java_net_Inet6Address_holder6; + + public final ObjectKlass java_net_Inet6Address$Inet6AddressHolder; + public final Field java_net_Inet6Address$Inet6AddressHolder_scope_ifname; + public final Field java_net_Inet6Address$Inet6AddressHolder_ipaddress; + + public final Method java_net_NetworkInterface_init; + public final Field java_net_NetworkInterface_displayName; + public final Field java_net_NetworkInterface_virtual; + public final Field java_net_NetworkInterface_bindings; + public final Field java_net_NetworkInterface_parent; + public final Field java_net_NetworkInterface_childs; + + public final ObjectKlass java_net_InetSocketAddress; + public final Method java_net_InetSocketAddress_init; + + // Checkstyle: resume field name check + + private LibNetMeta() { + java_net_InetAddress = knownKlass(EspressoSymbols.Types.java_net_InetAddress); + java_net_InetAddress_holder = java_net_InetAddress.requireDeclaredField(EspressoSymbols.Names.holder, EspressoSymbols.Types.java_net_InetAddress$InetAddressHolder); + java_net_InetAddress$InetAddressHolder = knownKlass(EspressoSymbols.Types.java_net_InetAddress$InetAddressHolder); + java_net_InetAddress$InetAddressHolder_address = java_net_InetAddress$InetAddressHolder.requireDeclaredField(EspressoSymbols.Names.address, EspressoSymbols.Types._int); + + java_net_Inet4Address = knownKlass(EspressoSymbols.Types.java_net_Inet4Address); + java_net_Inet4Address_init = java_net_Inet4Address.lookupDeclaredMethod(EspressoSymbols.Names._init_, EspressoSymbols.Signatures.java_net_Inet4Address_init_signature); + + java_net_Inet6Address = knownKlass(EspressoSymbols.Types.java_net_Inet6Address); + java_net_Inet6Address_init = java_net_Inet6Address.lookupDeclaredMethod(EspressoSymbols.Names._init_, EspressoSymbols.Signatures.java_net_Inet6Address_init_signature); + java_net_Inet6Address_holder6 = java_net_Inet6Address.requireDeclaredField(EspressoSymbols.Names.holder6, EspressoSymbols.Types.java_net_Inet6Address$Inet6AddressHolder); + java_net_Inet6Address$Inet6AddressHolder = knownKlass(EspressoSymbols.Types.java_net_Inet6Address$Inet6AddressHolder); + java_net_Inet6Address$Inet6AddressHolder_scope_ifname = java_net_Inet6Address$Inet6AddressHolder.requireDeclaredField(EspressoSymbols.Names.scope_ifname, + EspressoSymbols.Types.java_net_NetworkInterface); + java_net_Inet6Address$Inet6AddressHolder_ipaddress = java_net_Inet6Address$Inet6AddressHolder.requireDeclaredField(EspressoSymbols.Names.ipaddress, EspressoSymbols.Types._byte_array); + + java_net_InterfaceAddress = knownKlass(EspressoSymbols.Types.java_net_InterfaceAddress); + java_net_InterfaceAddress_init = java_net_InterfaceAddress.lookupDeclaredMethod(EspressoSymbols.Names._init_, EspressoSymbols.Signatures._void); + java_net_InterfaceAddress_address = java_net_InterfaceAddress.requireDeclaredField(EspressoSymbols.Names.address, EspressoSymbols.Types.java_net_InetAddress); + java_net_InterfaceAddress_broadcast = java_net_InterfaceAddress.requireDeclaredField(EspressoSymbols.Names.broadcast, EspressoSymbols.Types.java_net_Inet4Address); + java_net_InterfaceAddress_maskLength = java_net_InterfaceAddress.requireDeclaredField(EspressoSymbols.Names.maskLength, EspressoSymbols.Types._short); + + java_net_NetworkInterface_init = java_net_NetworkInterface.lookupDeclaredMethod(EspressoSymbols.Names._init_, EspressoSymbols.Signatures.java_net_NetworkInterface_init_signature); + java_net_NetworkInterface_displayName = java_net_NetworkInterface.requireDeclaredField(EspressoSymbols.Names.displayName, EspressoSymbols.Types.java_lang_String); + java_net_NetworkInterface_virtual = java_net_NetworkInterface.requireDeclaredField(EspressoSymbols.Names.virtual, EspressoSymbols.Types._boolean); + java_net_NetworkInterface_bindings = java_net_NetworkInterface.requireDeclaredField(EspressoSymbols.Names.bindings, EspressoSymbols.Types.java_net_InterfaceAddress_array); + java_net_NetworkInterface_parent = java_net_NetworkInterface.requireDeclaredField(EspressoSymbols.Names.parent, EspressoSymbols.Types.java_net_NetworkInterface); + java_net_NetworkInterface_childs = java_net_NetworkInterface.requireDeclaredField(EspressoSymbols.Names.childs, EspressoSymbols.Types.java_net_NetworkInterface_array); + + java_net_InetSocketAddress = knownKlass(EspressoSymbols.Types.java_net_InetSocketAddress); + java_net_InetSocketAddress_init = java_net_InetSocketAddress.lookupDeclaredMethod(EspressoSymbols.Names._init_, EspressoSymbols.Signatures.java_net_InetSocketAddress_init_signature); + } + + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsState.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsState.java index 15af2b9c3941..0d8be6701ccc 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsState.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsState.java @@ -23,32 +23,42 @@ package com.oracle.truffle.espresso.libs; import java.io.FileDescriptor; +import java.io.IOException; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.util.Objects; import java.util.zip.Inflater; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.TruffleLogger; import com.oracle.truffle.espresso.EspressoLanguage; -import com.oracle.truffle.espresso.io.FDAccess; -import com.oracle.truffle.espresso.io.TruffleIO; +import com.oracle.truffle.espresso.io.Throw; import com.oracle.truffle.espresso.jni.StrongHandles; import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.EspressoException; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; +import com.oracle.truffle.espresso.substitutions.JavaSubstitution; import com.oracle.truffle.espresso.substitutions.JavaType; +import com.oracle.truffle.espresso.vm.InterpreterToVM; public class LibsState { private static final TruffleLogger logger = TruffleLogger.getLogger(EspressoLanguage.ID, LibsState.class); - public static final FDAccess FD = new FDAccess() { - @Override - public @JavaType(FileDescriptor.class) StaticObject get(@JavaType(Object.class) StaticObject objectWithFD, TruffleIO io) { - return objectWithFD; - } - }; - private final StrongHandles handle2Inflater = new StrongHandles<>(); + public final LibsStateNet net; + + public LibsState(EspressoContext context, LibsMeta lMeta) { + this.net = (context.getLanguage().enableNetworking()) ? new LibsStateNet(context, lMeta) : null; + } + + public TruffleLogger getLogger() { + return logger; + } + public long handlifyInflater(Inflater i) { return handle2Inflater.handlify(i); } @@ -71,8 +81,119 @@ private static EspressoException throwInternalError() { return meta.throwExceptionWithMessage(meta.java_lang_InternalError, "the provided handle doesn't correspond to an Inflater"); } - public TruffleLogger getLogger() { - return logger; - } + public final class LibsStateNet { + private final EspressoContext context; + private final LibsMeta lMeta; + + public LibsStateNet(EspressoContext context, LibsMeta lMeta) { + this.context = context; + this.lMeta = lMeta; + } + + public @JavaType StaticObject convertInetAddr(InetAddress inetAddr) { + return convertInetAddr(inetAddr, StaticObject.NULL); + } + public @JavaType StaticObject convertInetAddr(InetAddress inetAddr, + @JavaType(NetworkInterface.class) StaticObject netIF) { + @JavaType(InetAddress.class) + StaticObject guestInetAddr = null; + if (inetAddr instanceof Inet4Address ipv4Addr) { + // IPv4 address + guestInetAddr = convertIpv4Addr(ipv4Addr); + } else if (inetAddr instanceof Inet6Address ipv6Addr) { + // IPv6 address + guestInetAddr = convertIpv6Addr(ipv6Addr, netIF); + } + Objects.requireNonNull(guestInetAddr); + return guestInetAddr; + } + + /** + * Converts a Guest InetAddress given as a StaticObject to a Host InetAddress. + * + * @param addr the Guest InetAddress + * @return The Host InetAddress + */ + public InetAddress fromGuestInetAddress(@JavaType(InetAddress.class) StaticObject addr) { + InetAddress inetAddress = null; + try { + if (InterpreterToVM.instanceOf(addr, lMeta.net.java_net_Inet4Address)) { + // ipv4 case retrieve int address, convert to bytes and use getByAddress + @JavaType(internalName = "Ljava/net/InetAddress$InetAddressHolder;") + StaticObject ipaHolder = lMeta.net.java_net_InetAddress_holder.getObject(addr); + int address = lMeta.net.java_net_InetAddress$InetAddressHolder_address.getInt(ipaHolder); + byte[] byteAddress = new byte[]{ + (byte) ((address >> 24) & 0xFF), + (byte) ((address >> 16) & 0xFF), + (byte) ((address >> 8) & 0xFF), + (byte) (address & 0xFF) + }; + inetAddress = InetAddress.getByAddress(byteAddress); + + } else if (InterpreterToVM.instanceOf(addr, lMeta.net.java_net_Inet6Address)) { + // ipv6 --> get the ip6 holder to retrieve the Ip6Address and use getByAddress + @JavaType(internalName = "Ljava/net/Inet6Address$Inet6AddressHolder;") + StaticObject ipv6Holder = lMeta.net.java_net_Inet6Address_holder6.getObject(addr); + @JavaType(byte[].class) + StaticObject ip6Addr = lMeta.net.java_net_Inet6Address$Inet6AddressHolder_ipaddress.getObject(ipv6Holder); + inetAddress = InetAddress.getByAddress(ip6Addr.unwrap(context.getLanguage())); + } else { + throw JavaSubstitution.shouldNotReachHere(); + } + return inetAddress; + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } + } + + private @JavaType StaticObject convertIpv4Addr(Inet4Address ipv4Addr) { + // in the original Native code the call the void constructor and set the address. To me + // providing full Information (Address and HostName) is the better approach. + @JavaType(Inet4Address.class) + StaticObject guestInetAddr = lMeta.net.java_net_Inet4Address.allocateInstance(context); + @JavaType(String.class) + StaticObject hostName = lMeta.getMeta().toGuestString(ipv4Addr.getHostName()); + @JavaType(byte[].class) + StaticObject addr = context.getAllocator().wrapArrayAs(lMeta.getMeta()._byte_array, ipv4Addr.getAddress()); + + lMeta.net.java_net_Inet4Address_init.invokeDirectSpecial( + /* this */ guestInetAddr, + /* hostName */ hostName, + /* address */ addr); + + return guestInetAddr; + } + + private @JavaType StaticObject convertIpv6Addr(Inet6Address ipv6Addr, + @JavaType(NetworkInterface.class) StaticObject netIF) { + // in the original Native code the call the void constructor and set the address. To me + // providing more Information is the better approach. + @JavaType(Inet6Address.class) + StaticObject guestInetAddr = lMeta.net.java_net_Inet6Address.allocateInstance(context); + @JavaType(String.class) + StaticObject hostName = lMeta.getMeta().toGuestString(ipv6Addr.getHostName()); + byte[] hostByteAddress = ipv6Addr.getAddress(); + @JavaType(byte[].class) + StaticObject addr = context.getAllocator().wrapArrayAs(lMeta.getMeta()._byte_array, hostByteAddress); + + lMeta.net.java_net_Inet6Address_init.invokeDirectSpecial( + /* this */ guestInetAddr, + /* hostName */ hostName, + /* address */ addr, + /* scopeId */ ipv6Addr.getScopeId()); + + // we also need to set the scope_ifName as in the native code (if applicable) + if (ipv6Addr.getScopeId() != 0 && netIF != StaticObject.NULL) { + @JavaType(internalName = "Ljava/net/Inet6Address$Inet6AddressHolder;") + StaticObject ipv6Holder = lMeta.net.java_net_Inet6Address_holder6.getObject(guestInetAddr); + lMeta.net.java_net_Inet6Address$Inet6AddressHolder_scope_ifname.setObject(ipv6Holder, netIF); + } + return guestInetAddr; + } + + public void setFDVal(@JavaType(FileDescriptor.class) StaticObject fd, int fdVal) { + context.getTruffleIO().java_io_FileDescriptor_fd.setInt(fd, fdVal); + } + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnet/LibNet.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnet/LibNet.java index 4f1a513e4d7d..abac13cafc59 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnet/LibNet.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnet/LibNet.java @@ -22,12 +22,11 @@ */ package com.oracle.truffle.espresso.libs.libnet; -import java.util.List; - import com.oracle.truffle.espresso.libs.Lib; import com.oracle.truffle.espresso.libs.Libs; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.substitutions.Collect; +import com.oracle.truffle.espresso.substitutions.JavaSubstitution; @Collect(Libs.class) public final class LibNet implements Lib.Factory { @@ -38,6 +37,6 @@ public String name() { @Override public Lib create(EspressoContext ctx) { - return new Lib(ctx, List.of() /*- LibNetCollector.getInstances(JavaSubstitution.Factory.class) */, name()); + return new Lib(ctx, LibNetCollector.getInstances(JavaSubstitution.Factory.class), name()); } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnet/impl/Target_java_net_Inet4Address.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnet/impl/Target_java_net_Inet4Address.java new file mode 100644 index 000000000000..dbdd419aae6a --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnet/impl/Target_java_net_Inet4Address.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libnet.impl; + +import java.net.Inet4Address; + +import com.oracle.truffle.espresso.libs.libnet.LibNet; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Substitution; + +@EspressoSubstitutions(value = Inet4Address.class, group = LibNet.class) +public final class Target_java_net_Inet4Address { + @Substitution + public static void init() { + // nop + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnet/impl/Target_java_net_Inet6Address.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnet/impl/Target_java_net_Inet6Address.java new file mode 100644 index 000000000000..b5b2a79054c2 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnet/impl/Target_java_net_Inet6Address.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libnet.impl; + +import java.net.Inet6Address; + +import com.oracle.truffle.espresso.libs.libnet.LibNet; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Substitution; + +@EspressoSubstitutions(value = Inet6Address.class, group = LibNet.class) +public final class Target_java_net_Inet6Address { + @Substitution + public static void init() { + // nop + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnet/impl/Target_java_net_Inet6AddressImpl.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnet/impl/Target_java_net_Inet6AddressImpl.java new file mode 100644 index 000000000000..34e6ce2aea0f --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnet/impl/Target_java_net_Inet6AddressImpl.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libnet.impl; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayDeque; +import java.util.Deque; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.espresso.io.Throw; +import com.oracle.truffle.espresso.libs.LibsMeta; +import com.oracle.truffle.espresso.libs.LibsState; +import com.oracle.truffle.espresso.libs.libnet.LibNet; +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; +import com.oracle.truffle.espresso.substitutions.JavaType; +import com.oracle.truffle.espresso.substitutions.Substitution; +import com.oracle.truffle.espresso.substitutions.Throws; + +@EspressoSubstitutions(type = "Ljava/net/Inet6AddressImpl;", group = LibNet.class) +public final class Target_java_net_Inet6AddressImpl { + @Substitution(hasReceiver = true) + @Throws(UnknownHostException.class) + @SuppressWarnings("unused") + public static @JavaType(String.class) StaticObject getLocalHostName(@JavaType(internalName = "Ljava/net/Inet6AddressImpl;") StaticObject self, + @Inject LibsMeta lMeta, @Inject EspressoContext context) { + try { + return lMeta.getMeta().toGuestString(InetAddress.getLocalHost().getHostName()); + } catch (UnknownHostException e) { + throw Throw.throwUnknownHostException(e.getMessage(), context); + } + } + + @Substitution(hasReceiver = true) + @Throws(UnknownHostException.class) + @SuppressWarnings("unused") + @TruffleBoundary + public static @JavaType(InetAddress[].class) StaticObject lookupAllHostAddr(@JavaType(internalName = "Ljava/net/Inet6AddressImpl;") StaticObject self, + @JavaType(String.class) StaticObject hostname, int characteristics, + @Inject LibsMeta lMeta, @Inject LibsState libsState, @Inject EspressoContext context) { + Meta meta = context.getMeta(); + if (hostname == StaticObject.NULL) { + throw lMeta.getMeta().throwExceptionWithMessage(meta.java_lang_NullPointerException, "host argument is null"); + } + try { + InetAddress[] allIps = InetAddress.getAllByName(meta.toHostString(hostname)); + // filter and reorder based on characteristics + boolean ipv4First = (characteristics & (1 << 2)) != 0; + boolean ipv6First = (characteristics & (1 << 3)) != 0; + boolean ipv6 = (characteristics & (1 << 1)) != 0; + boolean ipv4 = (characteristics & (1 << 0)) != 0; + Deque filteredIps = new ArrayDeque<>(allIps.length); + + if (!ipv6 && !ipv4) { + throw meta.throwExceptionWithMessage(meta.java_lang_IllegalArgumentException, "characteristics"); + } + + for (InetAddress ip : allIps) { + if (ip instanceof Inet4Address && ipv4) { + if (ipv4First) { + filteredIps.addFirst(libsState.net.convertInetAddr(ip)); + } else { + filteredIps.addLast(libsState.net.convertInetAddr(ip)); + } + } else if (ip instanceof Inet6Address && ipv6) { + if (ipv6First) { + filteredIps.addFirst(libsState.net.convertInetAddr(ip)); + } else { + filteredIps.addLast(libsState.net.convertInetAddr(ip)); + } + } + } + StaticObject[] arr = filteredIps.toArray(StaticObject.EMPTY_ARRAY); + return context.getAllocator().wrapArrayAs(lMeta.net.java_net_InetAddress.array(), arr); + } catch (UnknownHostException e) { + throw Throw.throwUnknownHostException(e.getMessage(), context); + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnet/impl/Target_java_net_InetAddress.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnet/impl/Target_java_net_InetAddress.java new file mode 100644 index 000000000000..d21acea362c7 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnet/impl/Target_java_net_InetAddress.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libnet.impl; + +import java.net.InetAddress; + +import com.oracle.truffle.espresso.libs.InformationLeak; +import com.oracle.truffle.espresso.libs.libnet.LibNet; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Inject; +import com.oracle.truffle.espresso.substitutions.Substitution; + +@EspressoSubstitutions(value = InetAddress.class, group = LibNet.class) +public final class Target_java_net_InetAddress { + @Substitution + public static void init() { + // nop + } + + @Substitution + public static boolean isIPv6Supported(@Inject InformationLeak iL) { + return iL.isIPv6Available0(); + + } + + @Substitution + public static boolean isIPv4Available(@Inject InformationLeak iL) { + return iL.isIPv4Available(); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnet/impl/Target_java_net_NetworkInterface.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnet/impl/Target_java_net_NetworkInterface.java new file mode 100644 index 000000000000..155a867971c9 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnet/impl/Target_java_net_NetworkInterface.java @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libnet.impl; + +import java.net.InetAddress; +import java.net.InterfaceAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.espresso.EspressoLanguage; +import com.oracle.truffle.espresso.io.Throw; +import com.oracle.truffle.espresso.libs.InformationLeak; +import com.oracle.truffle.espresso.libs.LibsMeta; +import com.oracle.truffle.espresso.libs.LibsState; +import com.oracle.truffle.espresso.libs.libnet.LibNet; +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; +import com.oracle.truffle.espresso.substitutions.JavaSubstitution; +import com.oracle.truffle.espresso.substitutions.JavaType; +import com.oracle.truffle.espresso.substitutions.Substitution; +import com.oracle.truffle.espresso.substitutions.Throws; + +@EspressoSubstitutions(value = NetworkInterface.class, group = LibNet.class) +public final class Target_java_net_NetworkInterface { + @Substitution + public static void init() { + // nop + } + + @Substitution + @Throws(SocketException.class) + public static @JavaType(NetworkInterface[].class) StaticObject getAll(@Inject EspressoContext ctx, @Inject EspressoLanguage lang, @Inject LibsMeta lMeta) { + if (!lang.enableNetworking()) { + StaticObject[] arr = StaticObject.EMPTY_ARRAY; + return ctx.getAllocator().wrapArrayAs(lMeta.java_net_NetworkInterface.array(), arr); + } + // maintain a map between host and guest InetAddresses to avoid redundant conversions. + ConcurrentHashMap hostGuestAddrMap = new ConcurrentHashMap<>(32); + + try { + Enumeration netIFs = NetworkInterface.getNetworkInterfaces(); + List guestNetIFs = new ArrayList<>(); + while (netIFs.hasMoreElements()) { + NetworkInterface netIF = netIFs.nextElement(); + guestNetIFs.add(convertNetIF(netIF, StaticObject.NULL, hostGuestAddrMap, ctx, lMeta)); + } + StaticObject[] arr = guestNetIFs.toArray(StaticObject.EMPTY_ARRAY); + return ctx.getAllocator().wrapArrayAs(lMeta.java_net_NetworkInterface.array(), arr); + } catch (SocketException e) { + throw Throw.throwSocketException(e, ctx); + } + } + + @Substitution + @Throws(SocketException.class) + @SuppressWarnings("unused") + public static @JavaType(NetworkInterface.class) StaticObject getByName0(@JavaType(String.class) StaticObject name) { + throw JavaSubstitution.unimplemented(); + } + + @Substitution + @Throws(SocketException.class) + @SuppressWarnings("unused") + public static @JavaType(NetworkInterface.class) StaticObject getByIndex0(int index) { + throw JavaSubstitution.unimplemented(); + } + + @Substitution + @Throws(SocketException.class) + @SuppressWarnings("unused") + public static boolean boundInetAddress0(@JavaType(InetAddress.class) StaticObject addr) { + throw JavaSubstitution.unimplemented(); + } + + @Substitution + @Throws(SocketException.class) + public static @JavaType(NetworkInterface.class) StaticObject getByInetAddress0(@JavaType(InetAddress.class) StaticObject addr, + @Inject LibsMeta lMeta, + @Inject EspressoContext ctx, + @Inject LibsState libsState) { + ctx.getInformationLeak().checkNetworkEnabled(); + InetAddress inetAddress = libsState.net.fromGuestInetAddress(addr); + try { + NetworkInterface netIF = NetworkInterface.getByInetAddress(inetAddress); + return convertNetIFSimple(netIF, ctx, lMeta); + } catch (SocketException e) { + throw Throw.throwSocketException(e, ctx); + } + } + + @Substitution + @TruffleBoundary + @Throws(SocketException.class) + public static boolean isUp0(@JavaType(String.class) StaticObject name, int ind, @Inject EspressoContext ctx) { + ctx.getInformationLeak().checkNetworkEnabled(); + try { + return getNetIF(name, ind, ctx).isUp(); + } catch (SocketException e) { + throw Throw.throwSocketException(e, ctx); + } + } + + @Substitution + @TruffleBoundary + @Throws(SocketException.class) + public static boolean isLoopback0(@JavaType(String.class) StaticObject name, int ind, @Inject EspressoContext ctx) { + ctx.getInformationLeak().checkNetworkEnabled(); + try { + return getNetIF(name, ind, ctx).isLoopback(); + } catch (SocketException e) { + throw Throw.throwSocketException(e, ctx); + } + } + + @Substitution + @Throws(SocketException.class) + @SuppressWarnings("unused") + public static boolean supportsMulticast0(@JavaType(String.class) StaticObject name, int ind) { + throw JavaSubstitution.unimplemented(); + } + + @Substitution + @Throws(SocketException.class) + @SuppressWarnings("unused") + public static boolean isP2P0(@JavaType(String.class) StaticObject name, int ind) { + throw JavaSubstitution.unimplemented(); + } + + @Substitution + @Throws(SocketException.class) + @SuppressWarnings("unused") + public static @JavaType(byte[].class) StaticObject getMacAddr0(@JavaType(byte[].class) StaticObject inAddr, @JavaType(String.class) StaticObject name, int ind, @Inject EspressoContext ctx, + @Inject InformationLeak iL) { + // The inAddr is not used. That's also the case in the native code. + iL.checkNetworkEnabled(); + byte[] macAddrHost = iL.getMacAddress(getNetIF(name, ind, ctx)); + if (macAddrHost == null) { + return StaticObject.NULL; + } + return ctx.getAllocator().wrapArrayAs(ctx.getMeta()._byte_array, macAddrHost); + } + + @Substitution + @Throws(SocketException.class) + @SuppressWarnings("unused") + public static int getMTU0(@JavaType(String.class) StaticObject name, int ind) { + throw JavaSubstitution.unimplemented(); + } + + private static @JavaType(NetworkInterface.class) StaticObject convertNetIFSimple(NetworkInterface netIF, + EspressoContext ctx, LibsMeta lMeta) { + return convertNetIF(netIF, StaticObject.NULL, new ConcurrentHashMap<>(8), ctx, lMeta); + } + + private static @JavaType(NetworkInterface.class) StaticObject convertNetIF(NetworkInterface netIF, + @JavaType(NetworkInterface.class) StaticObject parent, + ConcurrentHashMap hostGuestAddrMap, + EspressoContext ctx, LibsMeta lMeta) { + @JavaType(NetworkInterface.class) + StaticObject guestNetIF = lMeta.java_net_NetworkInterface.allocateInstance(ctx); + // convert constructor parameters + @JavaType(InetAddress[].class) + StaticObject guestInetAddrs = convertInetAddrs(netIF.getInetAddresses(), guestNetIF, hostGuestAddrMap, ctx, lMeta); + @JavaType(InterfaceAddress[].class) + StaticObject guestIFAddrs = convertIFAddrs(netIF.getInterfaceAddresses(), guestNetIF, hostGuestAddrMap, ctx, lMeta); + @JavaType(String.class) + StaticObject name = lMeta.getMeta().toGuestString(netIF.getName()); + + // constructor + lMeta.net.java_net_NetworkInterface_init.invokeDirectSpecial( + /* this */ guestNetIF, + /* name */ name, + /* index */ netIF.getIndex(), + /* addr */ guestInetAddrs); + + lMeta.net.java_net_NetworkInterface_displayName.setObject(guestNetIF, name); + lMeta.net.java_net_NetworkInterface_virtual.setBoolean(guestNetIF, netIF.isVirtual()); + lMeta.net.java_net_NetworkInterface_bindings.setObject(guestNetIF, guestIFAddrs); + + // convert childs if there are any + if (netIF.getSubInterfaces().hasMoreElements()) { + @JavaType(NetworkInterface[].class) + StaticObject guestChilds = convertChilds(netIF.getSubInterfaces(), guestNetIF, hostGuestAddrMap, ctx, lMeta); + lMeta.net.java_net_NetworkInterface_childs.setObject(guestNetIF, guestChilds); + } + + if (parent != StaticObject.NULL) { + lMeta.net.java_net_NetworkInterface_parent.setObject(guestNetIF, parent); + } + + return guestNetIF; + } + + private static @JavaType(InetAddress[].class) StaticObject convertInetAddrs(Enumeration inetAddrs, + @JavaType(NetworkInterface.class) StaticObject netIF, + ConcurrentHashMap hostGuestAddrMap, + EspressoContext ctx, LibsMeta lMeta) { + List guestInetAddrs = new ArrayList<>(); + while (inetAddrs.hasMoreElements()) { + InetAddress hostInetAddr = inetAddrs.nextElement(); + @JavaType(InetAddress.class) + StaticObject guestInetAddr = convertInetAddr(hostInetAddr, netIF, hostGuestAddrMap, ctx); + guestInetAddrs.add(guestInetAddr); + } + StaticObject[] arr = guestInetAddrs.toArray(StaticObject.EMPTY_ARRAY); + return ctx.getAllocator().wrapArrayAs(lMeta.net.java_net_InetAddress.array(), arr); + } + + private static @JavaType StaticObject convertInetAddr(InetAddress inetAddr, + @JavaType(NetworkInterface.class) StaticObject netIF, + ConcurrentHashMap hostGuestAddrMap, + EspressoContext ctx) { + // check cache first + if (hostGuestAddrMap.containsKey(inetAddr)) { + return hostGuestAddrMap.get(inetAddr); + } + // not in cache so do full conversion and cache it + @JavaType(InetAddress.class) + StaticObject guestInetAddr = ctx.getLibsState().net.convertInetAddr(inetAddr, netIF); + hostGuestAddrMap.put(inetAddr, guestInetAddr); + return guestInetAddr; + } + + private static @JavaType(InterfaceAddress[].class) StaticObject convertIFAddrs(List iFAddrs, + @JavaType(NetworkInterface.class) StaticObject netIF, + ConcurrentHashMap hostGuestAddrMap, + EspressoContext ctx, LibsMeta lMeta) { + List guestIFAddrs = new ArrayList<>(iFAddrs.size()); + for (InterfaceAddress iFAddr : iFAddrs) { + @JavaType(InterfaceAddress.class) + StaticObject guestIFAddr = convertIFAddr(iFAddr, netIF, hostGuestAddrMap, ctx, lMeta); + guestIFAddrs.add(guestIFAddr); + } + StaticObject[] arr = guestIFAddrs.toArray(StaticObject.EMPTY_ARRAY); + return ctx.getAllocator().wrapArrayAs(lMeta.net.java_net_InterfaceAddress.array(), arr); + } + + private static @JavaType(InterfaceAddress.class) StaticObject convertIFAddr(InterfaceAddress iFAddrs, + @JavaType(NetworkInterface.class) StaticObject netIF, + ConcurrentHashMap hostGuestAddrMap, + EspressoContext ctx, LibsMeta lMeta) { + @JavaType(InterfaceAddress.class) + StaticObject guestIFAddr = lMeta.net.java_net_InterfaceAddress.allocateInstance(ctx); + lMeta.net.java_net_InterfaceAddress_init.invokeDirectSpecial(guestIFAddr); + + @JavaType(InetAddress.class) + StaticObject addr = convertInetAddr(iFAddrs.getAddress(), netIF, hostGuestAddrMap, ctx); + lMeta.net.java_net_InterfaceAddress_address.setObject(guestIFAddr, addr); + + if (iFAddrs.getBroadcast() != null) { + @JavaType(InetAddress.class) + StaticObject broadcastAddr = convertInetAddr(iFAddrs.getBroadcast(), netIF, hostGuestAddrMap, ctx); + lMeta.net.java_net_InterfaceAddress_broadcast.setObject(guestIFAddr, broadcastAddr); + } + + lMeta.net.java_net_InterfaceAddress_maskLength.setShort(guestIFAddr, iFAddrs.getNetworkPrefixLength()); + return guestIFAddr; + } + + private static @JavaType(NetworkInterface[].class) StaticObject convertChilds(Enumeration childs, + @JavaType(NetworkInterface.class) StaticObject parent, + ConcurrentHashMap hostGuestAddrMap, + EspressoContext ctx, LibsMeta lMeta) { + List guestChilds = new ArrayList<>(); + while (childs.hasMoreElements()) { + NetworkInterface child = childs.nextElement(); + @JavaType(NetworkInterface.class) + StaticObject guestChild = convertNetIF(child, parent, hostGuestAddrMap, ctx, lMeta); + guestChilds.add(guestChild); + } + StaticObject[] arr = guestChilds.toArray(StaticObject.EMPTY_ARRAY); + return ctx.getAllocator().wrapArrayAs(lMeta.java_net_NetworkInterface.array(), arr); + } + + private static NetworkInterface getNetIF(@JavaType(String.class) StaticObject name, int ind, EspressoContext ctx) { + try { + NetworkInterface netIF = NetworkInterface.getByIndex(ind); + if (netIF == null) { + String hoststring = ctx.getMeta().toHostString(name); + netIF = hoststring == null ? null : NetworkInterface.getByName(hoststring); + } + if (netIF == null) { + Throw.throwSocketException("Didn't found a NetworkInterface associated with the given name and index", ctx); + } + return netIF; + } catch (SocketException e) { + throw Throw.throwSocketException(e, ctx); + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_Net.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_Net.java new file mode 100644 index 000000000000..f89ce0093267 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_Net.java @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libnio.impl; + +import static java.nio.channels.SelectionKey.OP_CONNECT; +import static java.nio.channels.SelectionKey.OP_READ; +import static java.nio.channels.SelectionKey.OP_WRITE; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.SocketOption; +import java.net.StandardSocketOptions; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +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.InformationLeak; +import com.oracle.truffle.espresso.libs.LibsMeta; +import com.oracle.truffle.espresso.libs.LibsState; +import com.oracle.truffle.espresso.libs.libnio.LibNio; +import com.oracle.truffle.espresso.meta.EspressoError; +import com.oracle.truffle.espresso.runtime.EspressoContext; +import com.oracle.truffle.espresso.runtime.EspressoException; +import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Inject; +import com.oracle.truffle.espresso.substitutions.JavaSubstitution; +import com.oracle.truffle.espresso.substitutions.JavaType; +import com.oracle.truffle.espresso.substitutions.Substitution; +import com.oracle.truffle.espresso.substitutions.Throws; +import com.oracle.truffle.espresso.substitutions.VersionFilter; + +@EspressoSubstitutions(type = "Lsun/nio/ch/Net;", group = LibNio.class) +public final class Target_sun_nio_ch_Net { + private static final short POLLIN = 0x1; + private static final short POLLOUT = 0x4; + private static final short POLLERR = 0x8; + private static final short POLLHUP = 0x10; + private static final short POLLNVAL = 0x20; + private static final short POLLCONN = 0x40; + + @Substitution + public static void initIDs() { + // nop + } + + @Substitution + public static boolean isIPv6Available0(@Inject InformationLeak iL) { + return iL.isIPv6Available0(); + + } + + @Substitution + public static short pollinValue(@Inject InformationLeak iL) { + iL.checkNetworkEnabled(); + return POLLIN; + } + + @Substitution + public static short polloutValue(@Inject InformationLeak iL) { + iL.checkNetworkEnabled(); + return POLLOUT; + } + + @Substitution + public static short pollerrValue(@Inject InformationLeak iL) { + iL.checkNetworkEnabled(); + return POLLERR; + } + + @Substitution + public static short pollhupValue(@Inject InformationLeak iL) { + iL.checkNetworkEnabled(); + return POLLHUP; + } + + @Substitution + public static short pollnvalValue(@Inject InformationLeak iL) { + iL.checkNetworkEnabled(); + return POLLNVAL; + } + + @Substitution + public static short pollconnValue(@Inject InformationLeak iL) { + iL.checkNetworkEnabled(); + return POLLCONN; + } + + @Substitution + public static int isExclusiveBindAvailable(@Inject InformationLeak iL) { + return iL.isExclusvieBindAvailable(); + } + + @Substitution + public static boolean isReusePortAvailable0(@Inject InformationLeak iL) { + return iL.isReusePortAvailable0(); + } + + @Substitution + @SuppressWarnings("unused") + public static int socket0(boolean preferIPv6, boolean stream, boolean reuse, + boolean fastLoopback, + @Inject TruffleIO io) { + return io.openSocket(preferIPv6, stream, reuse); + } + + @Substitution + @Throws(IOException.class) + @TruffleBoundary + public static int accept(@JavaType(FileDescriptor.class) StaticObject fd, @JavaType(FileDescriptor.class) StaticObject newfd, @JavaType(InetSocketAddress[].class) StaticObject isaa, + @Inject TruffleIO io, @Inject LibsState libsState, @Inject LibsMeta lMeta, @Inject EspressoContext ctx) { + // accept connection & populate fd. + SocketAddress[] clientSaArr = new SocketAddress[1]; + int retCode = io.accept(fd, FDAccess.forFileDescriptor(), newfd, clientSaArr, libsState); + if (retCode < 0) { + return retCode; + } + + if (clientSaArr[0] instanceof InetSocketAddress clientIsa) { + // convert to guest-object and populate argument-array + @JavaType(InetAddress.class) + StaticObject guestInetAddress = libsState.net.convertInetAddr(clientIsa.getAddress()); + + @JavaType(InetSocketAddress.class) + StaticObject guestSocket = lMeta.net.java_net_InetSocketAddress.allocateInstance(ctx); + // constructor call for guest-object-conversion + lMeta.net.java_net_InetSocketAddress_init.invokeDirectSpecial(guestSocket, guestInetAddress, + clientIsa.getPort()); + + @JavaType(InetSocketAddress.class) + StaticObject[] argArray = isaa.unwrap(ctx.getLanguage()); + // avoid check for non-emptiness since at every call site an array of length 1 is + // provided! + argArray[0] = guestSocket; + } else { + // remote Address is UnixDomainSocketAddress and IPC is not implemented. + throw JavaSubstitution.unimplemented(); + } + // return 1 as in the native Method + return 1; + } + + @Substitution + @Throws(IOException.class) + @SuppressWarnings("unused") + @TruffleBoundary + public static int connect0(boolean preferIPv6, @JavaType(FileDescriptor.class) StaticObject fd, + @JavaType(InetAddress.class) StaticObject remote, + int remotePort, @Inject TruffleIO io, @Inject LibsState libsState) { + InetAddress remoteAddress = libsState.net.fromGuestInetAddress(remote); + SocketAddress remoteSocket = new InetSocketAddress(remoteAddress, remotePort); + return io.connect(fd, FDAccess.forFileDescriptor(), remoteSocket) ? 1 : io.ioStatusSync.UNAVAILABLE; + } + + @Substitution + @Throws(IOException.class) + @TruffleBoundary + public static void shutdown(@JavaType(FileDescriptor.class) StaticObject fd, int how, + @Inject TruffleIO io) { + io.shutdownSocketChannel(fd, FDAccess.forFileDescriptor(), how); + } + + @Substitution(languageFilter = VersionFilter.Java25OrLater.class) + public static boolean shouldShutdownWriteBeforeClose0() { + // returns false on linux. + return false; + } + + @Substitution + @Throws(IOException.class) + public static void bind0(@JavaType(FileDescriptor.class) StaticObject fd, boolean preferIPv6, + boolean useExclBind, @JavaType(InetAddress.class) StaticObject addr, + int port, + @Inject TruffleIO io, @Inject LibsState libsState) { + io.bind(fd, FDAccess.forFileDescriptor(), preferIPv6, useExclBind, addr, port, libsState); + } + + @Substitution + @Throws(IOException.class) + public static void listen(@JavaType(FileDescriptor.class) StaticObject fd, int backlog, + @Inject TruffleIO io) { + io.listenTCP(fd, FDAccess.forFileDescriptor(), backlog); + } + + @Substitution + @Throws(IOException.class) + public static @JavaType(InetAddress.class) StaticObject localInetAddress(@JavaType(FileDescriptor.class) StaticObject fd, @Inject TruffleIO io) { + return io.getLocalAddress(fd, FDAccess.forFileDescriptor()); + } + + @Substitution + @Throws(IOException.class) + public static int localPort(@JavaType(FileDescriptor.class) StaticObject fd, @Inject TruffleIO io) { + return io.getPort(fd, FDAccess.forFileDescriptor()); + } + + @Substitution + @Throws(IOException.class) + @SuppressWarnings({"unused", "unchecked"}) + @TruffleBoundary + public static void setIntOption0(@JavaType(FileDescriptor.class) StaticObject fd, boolean mayNeedConversion, + int level, int opt, int arg, boolean isIPv6, + @Inject EspressoContext ctx, @Inject TruffleIO io) { + // We set the option over the public NetworkChannel API, thus the low-level Plattform + // specific arguments like mayNeedConversion and isIpv6 aren't needed + + // recover SocketOption and do Type-Conversion + SocketOption socketOption = getSocketOption(level, opt, ctx); + Class type = socketOption.type(); + if (type == Integer.class) { + SocketOption intSocketOption = (SocketOption) socketOption; + io.setSocketOption(fd, FDAccess.forFileDescriptor(), intSocketOption, arg); + } else if (type == Boolean.class) { + SocketOption boolSocketOption = (SocketOption) socketOption; + io.setSocketOption(fd, FDAccess.forFileDescriptor(), boolSocketOption, arg == 1 ? Boolean.TRUE : Boolean.FALSE); + } else { + // SocketOptions are Integer or Boolean + throw JavaSubstitution.shouldNotReachHere(); + } + } + + @Substitution + @Throws(IOException.class) + @SuppressWarnings({"unused", "unchecked"}) + @TruffleBoundary + public static int getIntOption0(@JavaType(FileDescriptor.class) StaticObject fd, boolean mayNeedConversion, + int level, int opt, + @Inject EspressoContext ctx, @Inject TruffleIO io) { + // We get the option over the public NetworkChannel API, thus the low-level Plattform + // mayNeedConversion and isIpv6 aren't needed + + // recover SocketOption and do Type-Conversion + SocketOption socketOption = getSocketOption(level, opt, ctx); + Class type = socketOption.type(); + if (type == Integer.class) { + SocketOption intSocketOption = (SocketOption) socketOption; + return io.getSocketOption(fd, FDAccess.forFileDescriptor(), intSocketOption); + } else if (type == Boolean.class) { + SocketOption boolSocketOption = (SocketOption) socketOption; + return io.getSocketOption(fd, FDAccess.forFileDescriptor(), boolSocketOption) ? 1 : 0; + } else { + // SocketOptions are Integer or Boolean + throw JavaSubstitution.shouldNotReachHere(); + } + } + + @Substitution + @Throws(IOException.class) + public static int available(@JavaType(FileDescriptor.class) StaticObject fd, @Inject TruffleIO io) { + return io.available(fd, FDAccess.forFileDescriptor()); + } + + private static SocketOption getSocketOption(int level, int opt, EspressoContext ctx) { + switch (level) { + case 0: + switch (opt) { + case 1: + return StandardSocketOptions.IP_TOS; + case 32: + return StandardSocketOptions.IP_MULTICAST_IF; + case 33: + return StandardSocketOptions.IP_MULTICAST_TTL; + case 34: + return StandardSocketOptions.IP_MULTICAST_LOOP; + } + break; + case 1: + switch (opt) { + case 2: + return StandardSocketOptions.SO_REUSEADDR; + case 6: + return StandardSocketOptions.SO_BROADCAST; + case 7: + return StandardSocketOptions.SO_SNDBUF; + case 8: + return StandardSocketOptions.SO_RCVBUF; + case 9: + return StandardSocketOptions.SO_KEEPALIVE; + case 10: + // Would be a ExtendedSocketOption.SO_OOBINLINE, however + // ExtendedOption are package private so we cannot easily access them. For + // Set and GetOption of Net Extended Option aren't accessed over the native + // world anyway thus we shouldn't reach here + throw JavaSubstitution.unimplemented(); + case 13: + return StandardSocketOptions.SO_LINGER; + case 15: + return StandardSocketOptions.SO_REUSEPORT; + } + break; + case 41: + switch (opt) { + case 17: + return StandardSocketOptions.IP_MULTICAST_IF; + case 18: + return StandardSocketOptions.IP_MULTICAST_TTL; + case 19: + return StandardSocketOptions.IP_MULTICAST_LOOP; + case 67: + return StandardSocketOptions.IP_TOS; + } + break; + } + if (level == 6 && opt == 1) { + return StandardSocketOptions.TCP_NODELAY; + } + throw Throw.throwUnsupported("Unsupported SocketOption: level = " + level + ", opt = " + opt, ctx); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java index 0bd0ebfd4d19..5fed0da3ac38 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java @@ -99,6 +99,7 @@ import com.oracle.truffle.espresso.io.TruffleIO; import com.oracle.truffle.espresso.jni.JNIHandles; import com.oracle.truffle.espresso.jni.JniEnv; +import com.oracle.truffle.espresso.libs.InformationLeak; import com.oracle.truffle.espresso.libs.LibsMeta; import com.oracle.truffle.espresso.libs.LibsState; import com.oracle.truffle.espresso.meta.EspressoError; @@ -202,6 +203,7 @@ public final class EspressoContext implements RuntimeAccess Date: Fri, 8 Aug 2025 14:51:10 +0200 Subject: [PATCH 07/26] Selector and Poller implementation for EspressoLibs Both implementations are very similar: In the constructor of the Poller/Selector (in the guest code) we generate a handle which in the native world gets associated with a host Selector. This handle is then always provided to the native methods, where we can easily retrieve the host Selector and implement the semantics. One of the most notable differences is that for the Poller all events are now one-shot meaning they should only be polled once. Additionally the EpollPoller sees new registerations in midst the poll operation, whereas the public Selector API does not. We solved that by waking up the Selector after new registerations. --- .../src/sun/nio/ch/DefaultPollerProvider.java | 38 ++++ .../src/sun/nio/ch/TrufflePoller.java | 68 ++++++ .../src/sun/nio/ch/DefaultPollerProvider.java | 46 ++++ .../src/sun/nio/ch/TrufflePoller.java | 71 ++++++ .../src/sun/nio/ch/DefaultPollerProvider.java | 44 ++++ .../sun/nio/ch/DefaultSelectorProvider.java | 30 +++ .../src/sun/nio/ch/TruffleSelector.java | 205 ++++++++++++++++++ .../sun/nio/ch/TruffleSelectorProvider.java | 33 +++ .../oracle/truffle/espresso/io/TruffleIO.java | 80 +++++++ .../truffle/espresso/libs/LibsState.java | 82 +++++++ .../libnio/impl/Target_sun_nio_ch_Net.java | 71 ++++++ .../impl/Target_sun_nio_ch_TrufflePoller.java | 119 ++++++++++ .../Target_sun_nio_ch_TruffleSelector.java | 146 +++++++++++++ 13 files changed, 1033 insertions(+) create mode 100644 espresso/src/com.oracle.truffle.espresso.io.jdk21/src/sun/nio/ch/DefaultPollerProvider.java create mode 100644 espresso/src/com.oracle.truffle.espresso.io.jdk21/src/sun/nio/ch/TrufflePoller.java create mode 100644 espresso/src/com.oracle.truffle.espresso.io.jdk25/src/sun/nio/ch/DefaultPollerProvider.java create mode 100644 espresso/src/com.oracle.truffle.espresso.io.jdk25/src/sun/nio/ch/TrufflePoller.java create mode 100644 espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/DefaultPollerProvider.java create mode 100644 espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/DefaultSelectorProvider.java create mode 100644 espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/TruffleSelector.java create mode 100644 espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/TruffleSelectorProvider.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_TrufflePoller.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_TruffleSelector.java diff --git a/espresso/src/com.oracle.truffle.espresso.io.jdk21/src/sun/nio/ch/DefaultPollerProvider.java b/espresso/src/com.oracle.truffle.espresso.io.jdk21/src/sun/nio/ch/DefaultPollerProvider.java new file mode 100644 index 000000000000..5e6065e56054 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.io.jdk21/src/sun/nio/ch/DefaultPollerProvider.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 sun.nio.ch; + +import java.io.IOException; + +public class DefaultPollerProvider extends PollerProvider { + + @Override + Poller readPoller() throws IOException { + return new TrufflePoller(true); + } + + @Override + Poller writePoller() throws IOException { + return new TrufflePoller(false); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.io.jdk21/src/sun/nio/ch/TrufflePoller.java b/espresso/src/com.oracle.truffle.espresso.io.jdk21/src/sun/nio/ch/TrufflePoller.java new file mode 100644 index 000000000000..654643742c8c --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.io.jdk21/src/sun/nio/ch/TrufflePoller.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 sun.nio.ch; + +import java.io.IOException; +import java.nio.channels.SelectionKey; + +public class TrufflePoller extends Poller { + private final int id; + private final int event; + + TrufflePoller(boolean read) { + super(read); + this.event = (read) ? SelectionKey.OP_READ : SelectionKey.OP_WRITE; + id = init(); + } + + @Override + void implRegister(int fdVal) throws IOException { + register(id, fdVal, event); + } + + @Override + void implDeregister(int fdVal) { + deregister(id, fdVal); + } + + @Override + int poll(int timeout) throws IOException { + doSelect(id, timeout); + int[] fds = getReadyFds(id); + for (int i = 0; i < fds.length; i++) { + polled(fds[i]); + } + return fds.length; + } + + static native int init(); + + static native void deregister(int id, int fd); + + static native void register(int id, int fd, int newEvents); + + static native int doSelect(int id, long timeout); + + static native int[] getReadyFds(int id); + +} diff --git a/espresso/src/com.oracle.truffle.espresso.io.jdk25/src/sun/nio/ch/DefaultPollerProvider.java b/espresso/src/com.oracle.truffle.espresso.io.jdk25/src/sun/nio/ch/DefaultPollerProvider.java new file mode 100644 index 000000000000..55eeb53dc85e --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.io.jdk25/src/sun/nio/ch/DefaultPollerProvider.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 sun.nio.ch; + +import java.io.IOException; + +public class DefaultPollerProvider extends PollerProvider { + @Override + Poller readPoller(boolean subPoller) throws IOException { + if (subPoller) { + // the concept of subPoller relies on virtual threads which are not supported thus we + // should not reach here. + throw new IllegalStateException("should not reach here"); + } + return new TrufflePoller(true); + } + + @Override + Poller writePoller(boolean subPoller) throws IOException { + if (subPoller) { + // same reasoning as above + throw new IllegalStateException("should not reach here"); + } + return new TrufflePoller(false); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.io.jdk25/src/sun/nio/ch/TrufflePoller.java b/espresso/src/com.oracle.truffle.espresso.io.jdk25/src/sun/nio/ch/TrufflePoller.java new file mode 100644 index 000000000000..488409ae59e6 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.io.jdk25/src/sun/nio/ch/TrufflePoller.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 sun.nio.ch; + +import java.io.IOException; +import java.nio.channels.SelectionKey; + +public class TrufflePoller extends Poller { + private final int id; + private final int event; + + TrufflePoller(boolean read) { + super(); + this.event = (read) ? SelectionKey.OP_READ : SelectionKey.OP_WRITE; + id = init(); + } + + @Override + void implRegister(int fdVal) throws IOException { + // this registeration must be one shot now + register(id, fdVal, event); + } + + @Override + void implDeregister(int fdVal, boolean polled) { + if (!polled) { + deregister(id, fdVal); + } + } + + @Override + int poll(int timeout) throws IOException { + doSelect(id, timeout); + int[] fds = getReadyFds(id); + for (int i = 0; i < fds.length; i++) { + polled(fds[i]); + } + return fds.length; + } + + static native int init(); + + static native void deregister(int id, int fd); + + static native void register(int id, int fd, int newEvents); + + static native int doSelect(int id, long timeout); + + static native int[] getReadyFds(int id); + +} diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/DefaultPollerProvider.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/DefaultPollerProvider.java new file mode 100644 index 000000000000..0bfc3b5778e1 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/DefaultPollerProvider.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 sun.nio.ch; + +import java.io.IOException; + +public class DefaultPollerProvider extends PollerProvider { + // needs to compile with 21+. Thus include all abstract methods of PollerProvider from 21+ + Poller readPoller(boolean subPoller) throws IOException { + return null; + } + + Poller writePoller(boolean subPoller) throws IOException { + return null; + } + + Poller readPoller() throws IOException { + return null; + } + + Poller writePoller() throws IOException { + return null; + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/DefaultSelectorProvider.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/DefaultSelectorProvider.java new file mode 100644 index 000000000000..915f876182f7 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/DefaultSelectorProvider.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 sun.nio.ch; + +public class DefaultSelectorProvider { + public static SelectorProviderImpl get() { + return new TruffleSelectorProvider(); + } +} + diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/TruffleSelector.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/TruffleSelector.java new file mode 100644 index 000000000000..6b3166891ecf --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/TruffleSelector.java @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 sun.nio.ch; + +import java.io.IOException; +import java.nio.channels.ClosedSelectorException; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.spi.SelectorProvider; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; + +public class TruffleSelector extends SelectorImpl { + private final int id; + private final Object updateLock = new Object(); + private final ConcurrentHashMap fdToKey = new ConcurrentHashMap<>(); + + protected TruffleSelector(SelectorProvider sp) { + super(sp); + id = init(); + } + + private void ensureOpen() { + if (!isOpen()) { + throw new ClosedSelectorException(); + } + } + + @Override + protected int doSelect(Consumer action, long timeout) throws IOException { + assert Thread.holdsLock(this); + boolean blocking = timeout != 0; + + processDeregisterQueue(); + + begin(blocking); + try { + doSelect(id, timeout); + } finally { + end(blocking); + } + + processDeregisterQueue(); + return processEvents(action); + } + + private int processEvents(Consumer action) { + assert Thread.holdsLock(this); + int numKeysUpdated = 0; + long[] fdAndOps = processEvents(id); + for (int i = 0; i < fdAndOps.length; i++) { + long fdAndOp = fdAndOps[i]; + int fd = (int) (fdAndOp & 0xFFFFFFFFL); + int rOps = (int) (fdAndOp >>> 32); + SelectionKeyImpl ski = fdToKey.get(fd); + if (ski != null) { + // the processReadyEvents expects the rOps to be in native form; + // There is no unified way of doing the Translation + // eg. OP_CONNECT gets translated to either PollIn and PollConn depending on the + // channel + // This should work since the underlying channel of ski is a SelChImpl + int rOpsNative = ((SelChImpl) ski.channel()).translateInterestOps(rOps); + numKeysUpdated += processReadyEvents(rOpsNative, ski, action); + } + } + return numKeysUpdated; + } + + private int translateOps(int rOps) { + short nativeOps = 0; + + if ((rOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0) { + nativeOps |= Net.pollinValue(); // You need to pass the correct InformationLeak instance + } + if ((rOps & SelectionKey.OP_WRITE) != 0) { + nativeOps |= Net.polloutValue(); // You need to pass the correct InformationLeak + // instance + } + if ((rOps & SelectionKey.OP_CONNECT) != 0) { + nativeOps |= Net.pollconnValue(); // You need to pass the correct InformationLeak + // instance + } + + // You might want to handle other conditions like error or hangup + // nativeOps |= pollerrValue(null); // You need to pass the correct InformationLeak instance + // nativeOps |= pollhupValue(null); // You need to pass the correct InformationLeak instance + + return nativeOps; + } + + @Override + public Selector wakeup() { + wakeup(id); + return this; + } + + @Override + protected void implClose() throws IOException { + assert Thread.holdsLock(this); + close(id); + } + + @Override + protected void implDereg(SelectionKeyImpl ski) throws IOException { + // nop + // Normally, the channel remains registered with the Selector until the next selection, + // at which point ImplDereg is called. However, this can cause issues in our implementation: + // if configureBlocking(true) is called on a "canceled" channel before the next selection, + // the channel is still internally linked to the selector. This will cause + // configureBlocking(true) to throw an IllegalBlockingModeException, since a channel with + // selection keys cannot be configured to blocking mode. To avoid this, we deregister the + // channel internally as soon as its key is canceled. + + } + + @Override + public void cancel(SelectionKeyImpl ski) { + super.cancel(ski); + // Immediately deregister the channel from the selector internally + assert !ski.isValid(); + int fd = ski.getFDVal(); + // In EPollSelectorImpl all accesses to sun.nio.ch.EPoll.ctl are synchronized thus we should + // do the same. + synchronized (updateLock) { + if (fdToKey.remove(fd) != null) { + deregister(id, fd); + } else { + assert ski.registeredEvents() == 0; + } + } + + } + + @Override + protected void setEventOps(SelectionKeyImpl ski) { + int fd = ski.getFDVal(); + SelectionKeyImpl previous = fdToKey.putIfAbsent(fd, ski); + assert (previous == null) || (previous == ski); + int newEvents = ski.interestOps(); + int registeredEvents = ski.registeredEvents(); + // Synchronization to native selector operations as in EPollSelectorImpl + synchronized (updateLock) { + if (newEvents != registeredEvents && ski.isValid()) { + // if (newEvents == 0) { + // deregister(id, fd); + // } + // We previously encountered unhandled CancelledKeyExceptions from io.register. The + // issue arose because our code canceled the HostSelectionKey internally, while the + // GuestSelectionKey remained valid. This allowed the Channel, Selector pair to be + // registered again, resulting in CancelledKeyExceptions. To fix this, we now update + // the interestOps of the HostSelectionKey when newEvents == 0, preventing it from + // being canceled. If the Channel, Selector pair is registered again, + // TruffleIO.register returns the same valid HostSelectionKey since it wasn't + // deregistered. The HostSelectionKey is now only invalidated when the + // GuestSelectionKey is canceled, which also invalidates the GuestSelectionKey + // itself. + if (registeredEvents == 0) { + // add to epoll + register(id, fd, newEvents); + } else { + // modify events + changeEvents(id, fd, newEvents); + } + ski.registeredEvents(newEvents); + } + } + } + + static native int init(); + + static native int doSelect(int id, long timeout); + + static native void deregister(int id, int fd); + + static native void register(int id, int fd, int newEvents); + + static native void wakeup(int id); + + static native void close(int id); + + static native void changeEvents(int id, int fd, int newEvents); + + static native long[] processEvents(int id); + +} diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/TruffleSelectorProvider.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/TruffleSelectorProvider.java new file mode 100644 index 000000000000..1890eefaa4dc --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/TruffleSelectorProvider.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 sun.nio.ch; + +import java.io.IOException; +import java.nio.channels.spi.AbstractSelector; + +public class TruffleSelectorProvider extends SelectorProviderImpl { + @Override + public AbstractSelector openSelector() throws IOException { + return new TruffleSelector(this); + } +} 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 aa4c2e8e3d82..c74a3fcf033d 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 @@ -47,9 +47,13 @@ import java.nio.channels.NonWritableChannelException; import java.nio.channels.ReadableByteChannel; import java.nio.channels.SeekableByteChannel; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.channels.WritableByteChannel; +import java.nio.channels.spi.AbstractSelectableChannel; import java.nio.file.OpenOption; import java.nio.file.StandardOpenOption; import java.nio.file.attribute.FileAttribute; @@ -276,6 +280,19 @@ public int openSocket(boolean preferIPv6, boolean stream, boolean reuse) { return createFDforChannel(channelWrapper); } + @TruffleBoundary + public void configureBlocking(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess, boolean blocking) { + Channel channel = Checks.ensureOpen(getChannel(getFD(self, fdAccess)), getContext()); + if (channel instanceof AbstractSelectableChannel selectableChannel) { + try { + selectableChannel.configureBlocking(blocking); + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } + } + } + @TruffleBoundary public int available(@JavaType(Object.class) StaticObject self, FDAccess fdAccess) { @@ -518,6 +535,54 @@ private void initSocketOptions(NetworkChannel networkChannel, boolean preferIPv6 } } + /** + * Works as specified by {@link TruffleIO#register(StaticObject, FDAccess, Selector, int)} but + * with the raw int fd. + */ + @TruffleBoundary + public SelectionKey register(int fd, Selector selector, int ops) { + Channel channel = Checks.ensureOpen(getChannel(fd), getContext()); + if (channel instanceof AbstractSelectableChannel selectableChannel) { + try { + // selectableChannel.configureBlocking(false); + if (selectableChannel.isBlocking()) { + throw Throw.throwIOException("Channel is blocking and thus can't be registered to a Selector", context); + } + context.getLibsState().net.checkValidOps(selectableChannel, ops); + return selectableChannel.register(selector, ops); + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } catch (RuntimeException e) { + // the method shouldn't throw anything but an IOException + throw Throw.throwIOException("In a native method the following exception occurred: " + e.getClass().toString() + ": " + e.getMessage(), context); + } + } else { + throw JavaSubstitution.shouldNotReachHere(); + } + } + + /** + * Registers a file descriptor with a selector for the specified operations. + * + * + *

+ * The file descriptor {@code fd} is associated with a channel, which must be an instance of + * {@link SelectableChannel}. If the channel is not selectable, an {@link IOException} is + * thrown. + *

+ * + * @param self A file descriptor holder + * @param fdAccess How to get the file descriptor from the holder + * @param selector The selector to register with + * @param ops the operations to monitor (e.g., {@link SelectionKey#OP_READ}, + * {@link SelectionKey#OP_WRITE}) + */ + public SelectionKey register(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess, Selector selector, int ops) { + int fd = getFD(getFileDesc(self, fdAccess)); + return register(fd, selector, ops); + } + /** * Opens a file and associates it with the given file descriptor holder. * @@ -701,6 +766,21 @@ public int readBytes(@JavaType(Object.class) StaticObject self, } } + /** + * @return whether the channel associated with the fd is in blocking mode. + */ + @TruffleBoundary + public boolean isBlocking(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess) { + Channel channel = Checks.ensureOpen(getChannel(getFD(self, fdAccess)), getContext()); + if (channel instanceof AbstractSelectableChannel selectableChannel) { + return selectableChannel.isBlocking(); + } else { + // the fd isn't an AbstractSelectableChannel + throw JavaSubstitution.shouldNotReachHere(); + } + } + /** * Reads a byte sequence from the file associated with the given file descriptor. * diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsState.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsState.java index 0d8be6701ccc..cca1518f616a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsState.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsState.java @@ -28,13 +28,18 @@ import java.net.Inet6Address; import java.net.InetAddress; import java.net.NetworkInterface; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; import java.util.zip.Inflater; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.TruffleLogger; import com.oracle.truffle.espresso.EspressoLanguage; import com.oracle.truffle.espresso.io.Throw; +import com.oracle.truffle.espresso.io.TruffleIO; import com.oracle.truffle.espresso.jni.StrongHandles; import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.runtime.EspressoContext; @@ -84,6 +89,11 @@ private static EspressoException throwInternalError() { public final class LibsStateNet { private final EspressoContext context; private final LibsMeta lMeta; + // used for guestHandle to hostSelector + private final StrongHandles handle2Selector = new StrongHandles<>(); + // mapping from guestHandle and channelFD to SelectionKey and the reversed one + private final ConcurrentHashMap hostSelectionKeys = new ConcurrentHashMap<>(); + private final ConcurrentHashMap selectionKeysToFd = new ConcurrentHashMap<>(); public LibsStateNet(EspressoContext context, LibsMeta lMeta) { this.context = context; @@ -195,5 +205,77 @@ public InetAddress fromGuestInetAddress(@JavaType(InetAddress.class) StaticObjec public void setFDVal(@JavaType(FileDescriptor.class) StaticObject fd, int fdVal) { context.getTruffleIO().java_io_FileDescriptor_fd.setInt(fd, fdVal); } + + public void checkValidOps(SelectableChannel selectableChannel, int ops) { + if ((~selectableChannel.validOps() & ops) != 0) { + throw Throw.throwIOException("operations associated with SelectionKey are not valid", context); + } + } + + @TruffleBoundary + public void putSelectionKey(int id, int fd, SelectionKey selKey) { + long key = ((long) id << 32) | (fd & 0xFFFFFFFFL); + SelectionKey previousSelKey = hostSelectionKeys.put(key, selKey); + Integer previousFd = selectionKeysToFd.put(selKey, fd); + // sanity check: SelectionKey <==> (SelectorId,ChannelFD) + assert previousSelKey == null || previousSelKey == selKey; + assert previousFd == null || previousFd == fd; + } + + @TruffleBoundary + public SelectionKey getSelectionKey(int id, int fd) { + long key = ((long) id << 32) | (fd & 0xFFFFFFFFL); + return hostSelectionKeys.get(key); + } + + @TruffleBoundary + public int getFdOfSelectionKey(SelectionKey key) { + return selectionKeysToFd.get(key); + } + + @TruffleBoundary + public void removeSelectionKey(int id, int fd) { + long key = ((long) id << 32) | (fd & 0xFFFFFFFFL); + SelectionKey selKey = hostSelectionKeys.remove(key); + selectionKeysToFd.remove(selKey); + } + + public long handlifySelector() { + try { + return handle2Selector.handlify(Selector.open()); + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } + } + + public Selector getSelector(int selectorId) { + Selector selector = handle2Selector.getObject(selectorId); + if (selector == null) { + // Breaks the invariant that all ids are associated with a selector. + throw JavaSubstitution.shouldNotReachHere(); + } + return selector; + } + + /** + * If the fd is already registered with the selector it updates the interestOps and + * otherwise registers the fd with the selector. + */ + public SelectionKey setInterestOpsOrRegister(int selectorId, int fd, int ops, TruffleIO io) { + Selector selector = getSelector(selectorId); + SelectionKey key = getSelectionKey(selectorId, fd); + if (key != null) { + checkValidOps(key.channel(), ops); + key.interestOps(ops); + } else { + key = io.register(fd, selector, ops); + putSelectionKey(selectorId, fd, key); + } + return key; + } + + public void freeSelector(int selectorId) { + handle2Selector.freeHandle(selectorId); + } } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_Net.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_Net.java index f89ce0093267..e8b1acf42ff3 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_Net.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_Net.java @@ -33,6 +33,8 @@ import java.net.SocketAddress; import java.net.SocketOption; import java.net.StandardSocketOptions; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.espresso.io.FDAccess; @@ -268,12 +270,81 @@ public static int getIntOption0(@JavaType(FileDescriptor.class) StaticObject fd, } } + @Substitution + @Throws(IOException.class) + public static boolean pollConnect(@JavaType(FileDescriptor.class) StaticObject fd, long timeout, @Inject TruffleIO io, @Inject EspressoContext ctx) { + int op = poll(fd, POLLCONN, timeout, io, ctx); + if (op == POLLCONN) { + return io.finishConnect(fd, FDAccess.forFileDescriptor()); + } + return false; + } + + @Substitution + @Throws(IOException.class) + @TruffleBoundary + public static int poll(@JavaType(FileDescriptor.class) StaticObject fd, int nativeOps, long timeout, @Inject TruffleIO io, @Inject EspressoContext ctx) { + try (Selector selector = Selector.open()) { + int ops = nativeToSelectorOps(nativeOps, ctx); + SelectionKey key = io.register(fd, FDAccess.forFileDescriptor(), selector, ops); + // do the select + if (timeout == 0) { + selector.selectNow(); + } else if (timeout == -1) { + selector.select(); + // there should be readyOps + assert key.readyOps() != 0; + } else if (timeout > 0) { + selector.select(timeout); + } + return selectorToNativeOps(key.readyOps(), ctx); + + } catch (IOException | EspressoException | EspressoError e) { + return io.ioStatusSync.THROWN; + } + + } + @Substitution @Throws(IOException.class) public static int available(@JavaType(FileDescriptor.class) StaticObject fd, @Inject TruffleIO io) { return io.available(fd, FDAccess.forFileDescriptor()); } + private static int selectorToNativeOps(int selectorOps, EspressoContext ctx) { + return switch (selectorOps) { + case (OP_READ) -> POLLIN; + case (OP_WRITE) -> POLLOUT; + case (OP_CONNECT) -> POLLCONN; + case (0) -> 0; + default -> + throw Throw.throwUnsupported("The following selector operation is not supported:" + selectorOps, ctx); + }; + } + + private static int nativeToSelectorOps(int nativeOps, EspressoContext ctx) { + int ops = 0; + if ((nativeOps & POLLIN) != 0) { + ops |= OP_READ; + } + if ((nativeOps & POLLOUT) != 0) { + ops |= OP_WRITE; + } + if ((nativeOps & POLLERR) != 0) { + throw Throw.throwUnsupported("currently we dont support polling POLLERR", ctx); + } + if ((nativeOps & POLLHUP) != 0) { + throw Throw.throwUnsupported("currently we dont support polling POLLHUP", ctx); + } + if ((nativeOps & POLLNVAL) != 0) { + throw Throw.throwUnsupported("currently we dont support polling POLLNVAL", ctx); + } + if ((nativeOps & POLLCONN) != 0) { + ops |= OP_CONNECT; + } + return ops; + } + private static SocketOption getSocketOption(int level, int opt, EspressoContext ctx) { switch (level) { case 0: diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_TrufflePoller.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_TrufflePoller.java new file mode 100644 index 000000000000..78c12db9c48e --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_TrufflePoller.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libnio.impl; + +import java.io.IOException; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.util.Set; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.espresso.io.Throw; +import com.oracle.truffle.espresso.io.TruffleIO; +import com.oracle.truffle.espresso.libs.LibsState; +import com.oracle.truffle.espresso.libs.libnio.LibNio; +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; +import com.oracle.truffle.espresso.substitutions.JavaSubstitution; +import com.oracle.truffle.espresso.substitutions.JavaType; +import com.oracle.truffle.espresso.substitutions.Substitution; + +@EspressoSubstitutions(type = "Lsun/nio/ch/TrufflePoller;", group = LibNio.class) +public final class Target_sun_nio_ch_TrufflePoller { + @Substitution + public static int init(@Inject LibsState libsState) { + return Math.toIntExact(libsState.net.handlifySelector()); + } + + @Substitution + public static void deregister(int id, int fd, @Inject LibsState libsState) { + SelectionKey selKey = libsState.net.getSelectionKey(id, fd); + if (selKey != null) { + selKey.cancel(); + libsState.net.removeSelectionKey(id, fd); + } + + } + + @Substitution + public static void register(int id, int fd, int newEvents, + @Inject LibsState libsState, + @Inject TruffleIO io) { + SelectionKey key = libsState.net.setInterestOpsOrRegister(id, fd, newEvents, io); + /* + * We need to call wake up since Epoll.wait() sees new registrations while waiting where as + * the selector does not. Therefore, we force the selector to wake up to see the + * registration. Note that the next select call will return immediately but this should be + * okay since it is caller is a while(true) loop see: sun.nio.ch.Poller.pollLoop + */ + key.selector().wakeup(); + } + + @Substitution + public static int doSelect(int id, long timeout, @Inject LibsState libsState, @Inject EspressoContext ctx) { + Selector selector = libsState.net.getSelector(id); + try { + if (timeout == 0) { + return selector.selectNow(); + } else if (timeout == -1) { + return selector.select(); + } else if (timeout > 0) { + return selector.select(timeout); + } + throw Throw.throwIOException("timeout should be >= -1", ctx); + } catch (IOException e) { + throw Throw.throwIOException(e, ctx); + } + } + + @Substitution + @TruffleBoundary + public static @JavaType(int[].class) StaticObject getReadyFds(int id, @Inject LibsState libsState, @Inject EspressoContext ctx, @Inject Meta meta) { + // We need to ensure that all events are one-shot, meaning they should only be polled once. + // This is done via synchronization and clearing the interestOps. + Selector selector = libsState.net.getSelector(id); + synchronized (selector) { + Set selectedKeys = selector.selectedKeys(); + int[] fds = new int[selectedKeys.size()]; + int i = 0; + for (SelectionKey key : selectedKeys) { + // Check if the key is valid + if (key.isValid()) { + int fd = libsState.net.getFdOfSelectionKey(key); + if (fd == 0) { + // indicates an invalid fd + throw JavaSubstitution.shouldNotReachHere(); + } + fds[i] = fd; + i++; + key.interestOps(0); + } + } + selectedKeys.clear(); + return ctx.getAllocator().wrapArrayAs(meta._int_array, fds); + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_TruffleSelector.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_TruffleSelector.java new file mode 100644 index 000000000000..fb67510262a6 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_TruffleSelector.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libnio.impl; + +import java.io.IOException; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.util.Set; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.espresso.io.Throw; +import com.oracle.truffle.espresso.io.TruffleIO; +import com.oracle.truffle.espresso.libs.LibsState; +import com.oracle.truffle.espresso.libs.libnio.LibNio; +import com.oracle.truffle.espresso.meta.Meta; +import com.oracle.truffle.espresso.runtime.EspressoContext; +import com.oracle.truffle.espresso.runtime.EspressoException; +import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Inject; +import com.oracle.truffle.espresso.substitutions.JavaSubstitution; +import com.oracle.truffle.espresso.substitutions.JavaType; +import com.oracle.truffle.espresso.substitutions.Substitution; + +@EspressoSubstitutions(type = "Lsun/nio/ch/TruffleSelector;", group = LibNio.class) +public final class Target_sun_nio_ch_TruffleSelector { + @Substitution + public static int init(@Inject LibsState libsState) { + return Math.toIntExact(libsState.net.handlifySelector()); + } + + @Substitution + public static void deregister(int id, int fd, @Inject LibsState libsState) { + SelectionKey selKey = libsState.net.getSelectionKey(id, fd); + if (selKey != null) { + selKey.cancel(); + libsState.net.removeSelectionKey(id, fd); + } + } + + @Substitution + public static void register(int id, int fd, int newEvents, + @Inject LibsState libsState, + @Inject TruffleIO io) { + Selector selector = libsState.net.getSelector(id); + try { + SelectionKey key = io.register(fd, selector, newEvents); + libsState.net.putSelectionKey(id, fd, key); + } catch (EspressoException e) { + // We should not throw exceptions here according to the Selector API. In + // sun.nio.ch.EPollSelectorImpl.processUpdateQueue error numbers from the native + // sun.nio.ch.EPoll.ctl are just ignored. So we do the same here. + libsState.getLogger().warning(() -> "In io.register the following exception was ingored: " + e.toString()); + } + } + + @Substitution + public static void changeEvents(int id, int fd, int newEvents, @Inject LibsState libsState) { + SelectionKey selectionKey = libsState.net.getSelectionKey(id, fd); + if (selectionKey == null) { + throw JavaSubstitution.shouldNotReachHere(); + } + libsState.net.checkValidOps(selectionKey.channel(), newEvents); + selectionKey.interestOps(newEvents); + } + + @Substitution + public static int doSelect(int id, long timeout, @Inject LibsState libsState, @Inject EspressoContext ctx) { + Selector selector = libsState.net.getSelector(id); + try { + if (timeout == 0) { + return selector.selectNow(); + } else if (timeout == -1) { + return selector.select(); + } else if (timeout > 0) { + return selector.select(timeout); + } + throw Throw.throwIOException("timeout should be >= -1", ctx); + } catch (IOException e) { + throw Throw.throwIOException(e, ctx); + } + } + + @Substitution + public static void wakeup(int id, @Inject LibsState libsState) { + Selector selector = libsState.net.getSelector(id); + selector.wakeup(); + } + + @Substitution + @TruffleBoundary + public static void close(int id, @Inject LibsState libsState, @Inject EspressoContext ctx) { + Selector selector = libsState.net.getSelector(id); + libsState.net.freeSelector(id); + try { + selector.close(); + } catch (IOException e) { + throw Throw.throwIOException(e, ctx); + } + } + + @Substitution + @TruffleBoundary + public static @JavaType(long[].class) StaticObject processEvents(int id, @Inject LibsState libsState, @Inject EspressoContext ctx, @Inject Meta meta) { + Selector selector = libsState.net.getSelector(id); + Set selectedKeys = selector.selectedKeys(); + long[] fdAndOps = new long[selectedKeys.size()]; + int i = 0; + for (SelectionKey key : selectedKeys) { + // Check if the key is valid + if (key.isValid()) { + // Get the ready ops for the key + int readyOps = key.readyOps(); + int fd = libsState.net.getFdOfSelectionKey(key); + if (fd == 0) { + throw JavaSubstitution.shouldNotReachHere(); + } + fdAndOps[i] = ((long) readyOps << 32) | (fd & 0xFFFFFFFFL); + i++; + } + } + selectedKeys.clear(); + return ctx.getAllocator().wrapArrayAs(meta._long_array, fdAndOps); + } + +} From 646a1c74ddec422969e24df924c18842230704a9 Mon Sep 17 00:00:00 2001 From: Elia Trachsel Date: Fri, 8 Aug 2025 15:14:12 +0200 Subject: [PATCH 08/26] IO and additional FileSystem substitutions: - DirectoryStream implementation by setting a host Iterator and Stream as hidden host reference then proxying all methods to the native world where we retrieve the host object and do the semantics of the function. - FileDisptacherImpl now has a TruffleDispatcher as a field to avoid code duplication. And generally the substitutions in the dispatcher classes just link against the appropriate methods in TruffleIO. - ReadV and WriteV implementation by retrieving the underlying direct bytebuffers of the native struct. --- .../DefaultAsynchronousChannelProvider.java | 42 +++++++ .../ch/DummyAsynchronousChannelProvider.java | 53 +++++++++ .../src/sun/nio/ch/FileDispatcherImpl.java | 23 ++-- .../src/sun/nio/ch/SocketDispatcher.java | 40 +++++++ .../src/sun/nio/ch/TruffleDispatcher.java | 64 +++++++++++ .../sun/nio/fs/TruffleFileSystemProvider.java | 1 + .../fs/TruffleFilteredDirectoryStream.java | 32 +++--- .../espresso/descriptors/EspressoSymbols.java | 3 + .../truffle/espresso/ffi/nfi/NativeUtils.java | 27 +++++ .../espresso/impl/LinkedKlassFieldLayout.java | 6 + .../com/oracle/truffle/espresso/io/Throw.java | 4 + .../oracle/truffle/espresso/io/TruffleIO.java | 92 +++++++++++++++ .../truffle/espresso/libs/LibsMeta.java | 19 ++++ .../Target_sun_nio_ch_FileDispatcherImpl.java | 17 ++- .../libnio/impl/Target_sun_nio_ch_IOUtil.java | 19 ++-- .../Target_sun_nio_ch_SocketDispatcher.java | 44 ++++++++ .../Target_sun_nio_ch_TruffleDispatcher.java | 79 +++++++++++++ ...nio_fs_TruffleFilteredDirectoryStream.java | 105 ++++++++++++++---- 18 files changed, 601 insertions(+), 69 deletions(-) create mode 100644 espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/DefaultAsynchronousChannelProvider.java create mode 100644 espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/DummyAsynchronousChannelProvider.java create mode 100644 espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/SocketDispatcher.java create mode 100644 espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/TruffleDispatcher.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_SocketDispatcher.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_TruffleDispatcher.java diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/DefaultAsynchronousChannelProvider.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/DefaultAsynchronousChannelProvider.java new file mode 100644 index 000000000000..4c7739a1c1b7 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/DefaultAsynchronousChannelProvider.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 sun.nio.ch; + +import java.nio.channels.spi.AsynchronousChannelProvider; + +public final class DefaultAsynchronousChannelProvider { + + /** + * Prevent instantiation. + */ + private DefaultAsynchronousChannelProvider() { + } + + /** + * Returns the default AsynchronousChannelProvider. + */ + public static AsynchronousChannelProvider create() { + return new DummyAsynchronousChannelProvider(); + } + +} diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/DummyAsynchronousChannelProvider.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/DummyAsynchronousChannelProvider.java new file mode 100644 index 000000000000..06bccb319c22 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/DummyAsynchronousChannelProvider.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 sun.nio.ch; + +import java.io.IOException; +import java.nio.channels.AsynchronousChannelGroup; +import java.nio.channels.AsynchronousServerSocketChannel; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.spi.AsynchronousChannelProvider; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadFactory; + +public class DummyAsynchronousChannelProvider extends AsynchronousChannelProvider { + @Override + public AsynchronousChannelGroup openAsynchronousChannelGroup(int nThreads, ThreadFactory threadFactory) throws IOException { + return null; + } + + @Override + public AsynchronousChannelGroup openAsynchronousChannelGroup(ExecutorService executor, int initialSize) throws IOException { + return null; + } + + @Override + public AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(AsynchronousChannelGroup group) throws IOException { + return null; + } + + @Override + public AsynchronousSocketChannel openAsynchronousSocketChannel(AsynchronousChannelGroup group) throws IOException { + return null; + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/FileDispatcherImpl.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/FileDispatcherImpl.java index 2e421bfa56a0..d0f08888f67c 100644 --- a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/FileDispatcherImpl.java +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/FileDispatcherImpl.java @@ -37,7 +37,8 @@ * This file must be compatible with 21+ */ final class FileDispatcherImpl extends sun.nio.ch.FileDispatcher { - + // to avoid code duplication of NativeDispatcher operations + private final TruffleDispatcher truffleDispatcher = new TruffleDispatcher(); static { sun.nio.ch.IOUtil.load(); } @@ -105,27 +106,27 @@ int setDirectIO(FileDescriptor fd, String path) { @Override int read(FileDescriptor fd, long address, int len) throws IOException { - return read0(fd, address, len); + return truffleDispatcher.read(fd, address, len); } @Override long readv(FileDescriptor fd, long address, int len) throws IOException { - return readv0(fd, address, len); + return truffleDispatcher.readv(fd, address, len); } @Override int write(FileDescriptor fd, long address, int len) throws IOException { - return write0(fd, address, len); + return truffleDispatcher.write(fd, address, len); } @Override long writev(FileDescriptor fd, long address, int len) throws IOException { - return writev0(fd, address, len); + return truffleDispatcher.writev(fd, address, len); } @Override void close(FileDescriptor fd) throws IOException { - close0(fd); + truffleDispatcher.close(fd); } @Override @@ -212,16 +213,6 @@ boolean isOther(FileDescriptor fd) throws IOException { private static native long size0(FileDescriptor fd) throws IOException; - private static native int read0(FileDescriptor fd, long address, int len) throws IOException; - - private static native int readv0(FileDescriptor fd, long address, int len) throws IOException; - - private static native int write0(FileDescriptor fd, long address, int len) throws IOException; - - private static native int writev0(FileDescriptor fd, long address, int len) throws IOException; - - private static native void close0(FileDescriptor fd) throws IOException; - private static native int pread0(FileDescriptor fd, long address, int len, long position) throws IOException; private static native int pwrite0(FileDescriptor fd, long address, int len, long position) throws IOException; diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/SocketDispatcher.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/SocketDispatcher.java new file mode 100644 index 000000000000..6bdb55c2f040 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/SocketDispatcher.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 sun.nio.ch; + +import java.io.FileDescriptor; +import java.io.IOException; + +class SocketDispatcher extends TruffleDispatcher { + @Override + int read(FileDescriptor fd, long address, int len) throws IOException { + int read = super.read(fd, address, len); + if (read == 0 && !isBlocking(fd)) { + return -2; + } + return read; + } + + private static native boolean isBlocking(FileDescriptor fd); + +} diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/TruffleDispatcher.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/TruffleDispatcher.java new file mode 100644 index 000000000000..2193704c4944 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/ch/TruffleDispatcher.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 sun.nio.ch; + +import java.io.FileDescriptor; +import java.io.IOException; + +class TruffleDispatcher extends NativeDispatcher { + @Override + int read(FileDescriptor fd, long address, int len) throws IOException { + return read0(fd, address, len); + } + + @Override + long readv(FileDescriptor fd, long address, int len) throws IOException { + return readv0(fd, address, len); + } + + @Override + int write(FileDescriptor fd, long address, int len) throws IOException { + return write0(fd, address, len); + } + + @Override + long writev(FileDescriptor fd, long address, int len) throws IOException { + return writev0(fd, address, len); + } + + @Override + void close(FileDescriptor fd) throws IOException { + close0(fd); + } + + private static native int read0(FileDescriptor fd, long address, int len) throws IOException; + + private static native int readv0(FileDescriptor fd, long address, int len) throws IOException; + + private static native int write0(FileDescriptor fd, long address, int len) throws IOException; + + private static native int writev0(FileDescriptor fd, long address, int len) throws IOException; + + private static native void close0(FileDescriptor fd) throws IOException; + +} diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFileSystemProvider.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFileSystemProvider.java index c061c90de497..d091756d77fa 100644 --- a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFileSystemProvider.java +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFileSystemProvider.java @@ -53,6 +53,7 @@ * This file must be compatible with 21+. */ class TruffleFileSystemProvider extends FileSystemProvider { + public static final int DEFAULT_FILE_PERMISSIONS_MASK = 511; static { // ensure 'nio' is loaded. Also loads 'net' as a side-effect. diff --git a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFilteredDirectoryStream.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFilteredDirectoryStream.java index eda8750cbdb4..521e5ad0c5b9 100644 --- a/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFilteredDirectoryStream.java +++ b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFilteredDirectoryStream.java @@ -31,17 +31,19 @@ import java.util.Objects; /** - * This file must be compatible with 21+. + * DirectoryStream implementation by setting a host iterator and stream as hidden host reference + * then proxying all methods to the native world where we retrieve the host object and do the + * semantics of the function. Note this file must be compatible with 21+. */ final class TruffleFilteredDirectoryStream implements DirectoryStream { private final TrufflePath truffleDir; - private final DirectoryStream stream; + private final DirectoryStream stream; private final DirectoryStream.Filter filter; /** - * Thin wrapper for a foreign (host) {@code Iterator}, cannot have any fields. + * Thin wrapper for a foreign (host) {@code Iterator}, cannot have any fields. */ - private static final class ForeignIterator implements Iterator { + private static final class ForeignIterator implements Iterator { private ForeignIterator() { // only foreign wrappers allowed @@ -53,7 +55,7 @@ public boolean hasNext() { } @Override - public Object next() { + public String next() { return next0(this); } } @@ -61,7 +63,7 @@ public Object next() { /** * Thin wrapper for a foreign (host) DirectoryStream, cannot have any fields. */ - private static final class ForeignDirectoryStream implements DirectoryStream { + private static final class ForeignDirectoryStream implements DirectoryStream { private ForeignDirectoryStream() { // only foreign wrappers @@ -73,7 +75,7 @@ public void close() throws IOException { } @Override - public Iterator iterator() { + public Iterator iterator() { return iterator0(this, ForeignIterator.class); } } @@ -85,7 +87,7 @@ static DirectoryStream create(TrufflePath truffleDir, Filter return new TruffleFilteredDirectoryStream(truffleDir, directoryStream0(truffleDir, ForeignDirectoryStream.class), filter); } - private TruffleFilteredDirectoryStream(TrufflePath dir, DirectoryStream stream, Filter filter) { + private TruffleFilteredDirectoryStream(TrufflePath dir, DirectoryStream stream, Filter filter) { this.truffleDir = Objects.requireNonNull(dir); this.filter = Objects.requireNonNull(filter); this.stream = Objects.requireNonNull(stream); @@ -94,7 +96,7 @@ private TruffleFilteredDirectoryStream(TrufflePath dir, DirectoryStream @Override public Iterator iterator() { - return MapFilterIterator.mapThenFilter(stream.iterator(), tf -> toTrufflePath0(tf, truffleDir.getTruffleFileSystem()), + return MapFilterIterator.mapThenFilter(stream.iterator(), tf -> new TrufflePath((TruffleFileSystem) truffleDir.getFileSystem(), truffleDir.resolve(tf).toString()), path -> { try { return filter.accept(path); @@ -116,17 +118,15 @@ private static RuntimeException sneakyThrow(Throwable ex) // region native methods - private static native DirectoryStream directoryStream0(TrufflePath dir, Class directoryStreamClass) throws IOException; + private static native DirectoryStream directoryStream0(TrufflePath dir, Class directoryStreamClass) throws IOException; - private static native boolean hasNext0(Iterator iterator); + private static native boolean hasNext0(Iterator iterator); - private static native Object next0(Iterator iterator); + private static native String next0(Iterator iterator); - private static native void close0(DirectoryStream directoryStream) throws IOException; + private static native void close0(DirectoryStream directoryStream) throws IOException; - private static native Iterator iterator0(DirectoryStream directoryStream, Class iteratorClass); - - private static native TrufflePath toTrufflePath0(Object truffleFile, TruffleFileSystem truffleFileSystem); + private static native Iterator iterator0(DirectoryStream directoryStream, Class iteratorClass); // endregion native methods } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java index de0248fdc5f8..bf1349d8aa72 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java @@ -182,6 +182,9 @@ public static void ensureInitialized() { public static final Symbol sun_net_ConnectionResetException = SYMBOLS.putType("Lsun/net/ConnectionResetException;"); public static final Symbol java_net_UnknownHostException = SYMBOLS.putType("Ljava/net/UnknownHostException;"); public static final Symbol sun_nio_ch_IOStatus = SYMBOLS.putType("Lsun/nio/ch/IOStatus;"); + // libnio + public static final Symbol sun_nio_fs_TruffleFilteredDirectoryStream$ForeignDirectoryStream = SYMBOLS.putType("Lsun/nio/fs/TruffleFilteredDirectoryStream$ForeignDirectoryStream;"); + public static final Symbol sun_nio_fs_TruffleFilteredDirectoryStream$ForeignIterator = SYMBOLS.putType("Lsun/nio/fs/TruffleFilteredDirectoryStream$ForeignIterator;"); // URL class loader public static final Symbol java_net_URLClassLoader = SYMBOLS.putType("Ljava/net/URLClassLoader;"); public static final Symbol java_net_URL = SYMBOLS.putType("Ljava/net/URL;"); 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 311c6d5e63d8..0176ea630258 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,6 +22,8 @@ */ package com.oracle.truffle.espresso.ffi.nfi; +import static com.oracle.truffle.espresso.substitutions.standard.Target_sun_misc_Unsafe.ADDRESS_SIZE; + import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -193,4 +195,29 @@ public static String fromUTF8Ptr(long rawBytesPtr) { public static ByteBuffer allocateDirect(int capacity) { return ByteBuffer.allocateDirect(capacity).order(ByteOrder.nativeOrder()); } + + @TruffleBoundary + public static ByteBuffer[] getByteBuffersFromIOVec(long address, int len) { + // constants + int lenOffset = ADDRESS_SIZE; + int sizeOfIOVec = (short) (ADDRESS_SIZE * 2); + int baseOffset = 0; + + long curIOVecAddr = address; + long nextAddr; + long nextLen; + ByteBuffer[] buffs = new ByteBuffer[len]; + for (int i = 0; i < len; i++) { + if (ADDRESS_SIZE == 4) { + nextAddr = UNSAFE.getInt(curIOVecAddr + baseOffset); + nextLen = UNSAFE.getInt(curIOVecAddr + lenOffset); + } else { + nextAddr = UNSAFE.getLong(curIOVecAddr + baseOffset); + nextLen = UNSAFE.getLong(curIOVecAddr + lenOffset); + } + buffs[i] = directByteBuffer(nextAddr, nextLen); + curIOVecAddr += sizeOfIOVec; + } + return buffs; + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java index 3ddde3b652e3..fad920f5368b 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java @@ -278,6 +278,12 @@ private static class HiddenField { entry(Types.sun_nio_fs_TrufflePath, new HiddenField[]{ new HiddenField(Names.HIDDEN_TRUFFLE_FILE, Types.java_lang_Object, EspressoLanguage::useEspressoLibs, ACC_FINAL) }), + entry(Types.sun_nio_fs_TruffleFilteredDirectoryStream$ForeignDirectoryStream, new HiddenField[]{ + new HiddenField(Names.HIDDEN_HOST_REFERENCE, Types.java_lang_Object, EspressoLanguage::useEspressoLibs, ACC_FINAL) + }), + entry(Types.sun_nio_fs_TruffleFilteredDirectoryStream$ForeignIterator, new HiddenField[]{ + new HiddenField(Names.HIDDEN_HOST_REFERENCE, Types.java_lang_Object, EspressoLanguage::useEspressoLibs, ACC_FINAL) + }), entry(Types.java_util_zip_CRC32, new HiddenField[]{ new HiddenField(Names.HIDDEN_CRC32, Types.java_lang_Object, EspressoLanguage::useEspressoLibs, ACC_FINAL) }) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/Throw.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/Throw.java index 6046c9d143d2..1cf8aca93d6e 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/Throw.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/Throw.java @@ -58,6 +58,10 @@ public static EspressoException throwFileNotFoundException(String message, Espre throw context.getMeta().throwExceptionWithMessage(context.getTruffleIO().java_io_FileNotFoundException, message); } + public static EspressoException throwIllegalArgumentException(String message, EspressoContext context) { + throw context.getMeta().throwExceptionWithMessage(context.getMeta().java_lang_IllegalArgumentException, message); + } + public static EspressoException throwIOException(String message, EspressoContext context) { throw context.getMeta().throwExceptionWithMessage(context.getTruffleIO().java_io_IOException, message); } 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 c74a3fcf033d..6c2ea100769c 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 @@ -42,10 +42,13 @@ import java.nio.channels.Channels; import java.nio.channels.ClosedByInterruptException; import java.nio.channels.DatagramChannel; +import java.nio.channels.GatheringByteChannel; import java.nio.channels.NetworkChannel; import java.nio.channels.NonReadableChannelException; import java.nio.channels.NonWritableChannelException; +import java.nio.channels.Pipe; import java.nio.channels.ReadableByteChannel; +import java.nio.channels.ScatteringByteChannel; import java.nio.channels.SeekableByteChannel; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; @@ -280,6 +283,32 @@ public int openSocket(boolean preferIPv6, boolean stream, boolean reuse) { return createFDforChannel(channelWrapper); } + /** + * Temporary Method to open a pipe (since we return a HostPipe here). + * + * @param blocking the blocking mode of the created pipe. + * @return Returns two file descriptors for a pipe encoded in a long. The read end of the pipe + * is returned in the high 32 bits, while the write end is returned in the low 32 bits. + */ + @TruffleBoundary + public long openPipe(boolean blocking) { + // opening the channel + try { + Pipe pipe = Pipe.open(); + Pipe.SinkChannel sink = pipe.sink(); + Pipe.SourceChannel source = pipe.source(); + sink.configureBlocking(blocking); + source.configureBlocking(blocking); + ChannelWrapper channelWrapperSink = new ChannelWrapper(sink, 1); + ChannelWrapper channelWrapperSource = new ChannelWrapper(source, 1); + int sourceFd = createFDforChannel(channelWrapperSource); // fd[0] + int sinkFd = createFDforChannel(channelWrapperSink); // fd[1] + return ((long) sourceFd << 32) | (sinkFd & 0xFFFFFFFFL); + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } + } + @TruffleBoundary public void configureBlocking(@JavaType(Object.class) StaticObject self, FDAccess fdAccess, boolean blocking) { @@ -697,6 +726,69 @@ public int writeBytes(int fd, return writeBytesImpl(getWritableChannel(fd), bytes, off, len, context); } + /** + * Writes the content of the 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. + * @return The number of bytes written, possibly zero. + * @see java.nio.channels.GatheringByteChannel#write(ByteBuffer[]) + */ + @TruffleBoundary + public long writeByteBuffers(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess, + ByteBuffer[] buffers) { + Channel channel = Checks.ensureOpen(getChannel(getFD(self, fdAccess)), getContext()); + if (channel instanceof GatheringByteChannel gatheringByteChannel) { + try { + return gatheringByteChannel.write(buffers); + } 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; + } + } + + /** + * Reads the content of the file associated with the given file descriptor into the provided + * ByteBuffers sequentially. + * + * @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. + * @return The number of bytes written, possibly zero. + * @see java.nio.channels.ScatteringByteChannel#read(ByteBuffer) + */ + @TruffleBoundary + public long readByteBuffers(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess, + ByteBuffer[] buffers) { + StaticObject fileDesc = getFileDesc(self, fdAccess); + Channel channel = Checks.ensureOpen(getChannel(getFD(fileDesc)), getContext()); + if (channel instanceof ScatteringByteChannel scatteringByteChannel) { + try { + return scatteringByteChannel.read(buffers); + } 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; + } + } + /** * Reads a single byte from the file associated with the given file descriptor holder. * diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsMeta.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsMeta.java index 90ab0550fccd..fe0f83042c1e 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsMeta.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsMeta.java @@ -38,7 +38,15 @@ public final class LibsMeta implements ContextAccess { private final EspressoContext context; private final Meta meta; + // Checkstyle: stop field name check + // libnio + public final ObjectKlass sun_nio_fs_TruffleFilteredDirectoryStream$ForeignDirectoryStream; + public final Field sun_nio_fs_TruffleFilteredDirectoryStream$ForeignDirectoryStream_HIDDEN_HOST_REFERENCE; + public final Method sun_nio_fs_TruffleFilteredDirectoryStream$ForeignDirectoryStream_init; + public final ObjectKlass sun_nio_fs_TruffleFilteredDirectoryStream$ForeignIterator; + public final Field sun_nio_fs_TruffleFilteredDirectoryStream$ForeignIterator_HIDDEN_HOST_REFERENCE; + public final Method sun_nio_fs_TruffleFilteredDirectoryStream$ForeignIterator_init; // libzip public final ObjectKlass java_util_zip_CRC32; public final Field HIDDEN_CRC32; @@ -61,6 +69,17 @@ public LibsMeta(EspressoContext ctx) { this.context = ctx; this.meta = context.getMeta(); + // libnio + sun_nio_fs_TruffleFilteredDirectoryStream$ForeignDirectoryStream = knownKlass(EspressoSymbols.Types.sun_nio_fs_TruffleFilteredDirectoryStream$ForeignDirectoryStream); + sun_nio_fs_TruffleFilteredDirectoryStream$ForeignDirectoryStream_init = sun_nio_fs_TruffleFilteredDirectoryStream$ForeignDirectoryStream.lookupDeclaredMethod(EspressoSymbols.Names._init_, + EspressoSymbols.Signatures._void); + sun_nio_fs_TruffleFilteredDirectoryStream$ForeignIterator = knownKlass(EspressoSymbols.Types.sun_nio_fs_TruffleFilteredDirectoryStream$ForeignIterator); + sun_nio_fs_TruffleFilteredDirectoryStream$ForeignIterator_init = sun_nio_fs_TruffleFilteredDirectoryStream$ForeignIterator.lookupDeclaredMethod(EspressoSymbols.Names._init_, + EspressoSymbols.Signatures._void); + sun_nio_fs_TruffleFilteredDirectoryStream$ForeignDirectoryStream_HIDDEN_HOST_REFERENCE = sun_nio_fs_TruffleFilteredDirectoryStream$ForeignDirectoryStream.requireHiddenField( + EspressoSymbols.Names.HIDDEN_HOST_REFERENCE); + sun_nio_fs_TruffleFilteredDirectoryStream$ForeignIterator_HIDDEN_HOST_REFERENCE = sun_nio_fs_TruffleFilteredDirectoryStream$ForeignIterator.requireHiddenField( + EspressoSymbols.Names.HIDDEN_HOST_REFERENCE); // libzip java_util_zip_CRC32 = knownKlass(EspressoSymbols.Types.java_util_zip_CRC32); HIDDEN_CRC32 = diff().field(ALL, EspressoSymbols.Names.HIDDEN_CRC32, EspressoSymbols.Types._int).maybeHiddenfield(java_util_zip_CRC32); 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 7e0f34555fab..56eeefaa3122 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 @@ -31,6 +31,8 @@ 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.espresso.ffi.NativeAccess; +import com.oracle.truffle.espresso.ffi.RawPointer; import com.oracle.truffle.espresso.ffi.nfi.NativeUtils; import com.oracle.truffle.espresso.io.FDAccess; import com.oracle.truffle.espresso.io.Throw; @@ -97,8 +99,9 @@ public static long map0(@JavaType(FileDescriptor.class) StaticObject fd, int pro @Substitution @SuppressWarnings("unused") - public static int unmap0(long address, long length) { - throw JavaSubstitution.unimplemented(); + public static int unmap0(long address, long length, @Inject NativeAccess nativeAccess) { + nativeAccess.freeMemory(RawPointer.create(address)); + return 0; } @Substitution @@ -196,8 +199,8 @@ public static int writev0(@JavaType(FileDescriptor.class) StaticObject fd, long @Substitution @Throws(IOException.class) @SuppressWarnings("unused") - public static void close0(@JavaType(FileDescriptor.class) StaticObject fd) { - throw JavaSubstitution.unimplemented(); + public static void close0(@JavaType(FileDescriptor.class) StaticObject fd, @Inject TruffleIO io) { + io.close(fd, FDAccess.forFileDescriptor()); } @Substitution @@ -235,4 +238,10 @@ public static void dup0(@JavaType(FileDescriptor.class) StaticObject fd1, @JavaT public static int setDirect0(@JavaType(FileDescriptor.class) StaticObject fd, @JavaType(String.class) StaticObject path) { throw JavaSubstitution.unimplemented(); } + + @Substitution + @Throws(IOException.class) + public static int available0(@JavaType(FileDescriptor.class) StaticObject fd, @Inject TruffleIO io) { + return io.available(fd, FDAccess.forFileDescriptor()); + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_IOUtil.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_IOUtil.java index 6c83a27ebb3f..6894ea54c904 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_IOUtil.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_IOUtil.java @@ -26,12 +26,12 @@ import java.io.IOException; import com.oracle.truffle.espresso.io.Checks; +import com.oracle.truffle.espresso.io.FDAccess; import com.oracle.truffle.espresso.io.TruffleIO; import com.oracle.truffle.espresso.libs.libnio.LibNio; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; import com.oracle.truffle.espresso.substitutions.Inject; -import com.oracle.truffle.espresso.substitutions.JavaSubstitution; import com.oracle.truffle.espresso.substitutions.JavaType; import com.oracle.truffle.espresso.substitutions.Substitution; import com.oracle.truffle.espresso.substitutions.Throws; @@ -71,8 +71,8 @@ public final class Target_sun_nio_ch_IOUtil { @Substitution @Throws(IOException.class) @SuppressWarnings("unused") - static long makePipe(boolean blocking) { - throw JavaSubstitution.unimplemented(); + static long makePipe(boolean blocking, @Inject TruffleIO io) { + return io.openPipe(blocking); } @Substitution @@ -81,6 +81,12 @@ static int write1(int fd, byte b, @Inject TruffleIO io) { return io.writeBytes(fd, new byte[]{b}, 0, 1); } + @Substitution + @Throws(IOException.class) + public static void configureBlocking(@JavaType(FileDescriptor.class) StaticObject fd, boolean blocking, @Inject TruffleIO io) { + io.configureBlocking(fd, FDAccess.forFileDescriptor(), blocking); + } + @Substitution @Throws(IOException.class) static boolean drain(int fd, @Inject TruffleIO io) { @@ -97,13 +103,6 @@ static int drain1(int fd, @Inject TruffleIO io) { return 0; } - @Substitution - @Throws(IOException.class) - @SuppressWarnings("unused") - public static void configureBlocking(@JavaType(FileDescriptor.class) StaticObject fd, boolean blocking) { - throw JavaSubstitution.unimplemented(); - } - @Substitution public static int fdVal(@JavaType(FileDescriptor.class) StaticObject fd, @Inject TruffleIO io) { Checks.nullCheck(fd, io); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_SocketDispatcher.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_SocketDispatcher.java new file mode 100644 index 000000000000..31cc5d54fd11 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_SocketDispatcher.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libnio.impl; + +import java.io.FileDescriptor; + +import com.oracle.truffle.espresso.io.FDAccess; +import com.oracle.truffle.espresso.io.TruffleIO; +import com.oracle.truffle.espresso.libs.libnio.LibNio; +import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Inject; +import com.oracle.truffle.espresso.substitutions.JavaType; +import com.oracle.truffle.espresso.substitutions.Substitution; + +@EspressoSubstitutions(type = "Lsun/nio/ch/SocketDispatcher;", group = LibNio.class) +public final class Target_sun_nio_ch_SocketDispatcher { + + @Substitution + public static boolean isBlocking(@JavaType(FileDescriptor.class) StaticObject fd, + @Inject TruffleIO io) { + return io.isBlocking(fd, FDAccess.forFileDescriptor()); + } +} 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 new file mode 100644 index 000000000000..9e67855c31dc --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_ch_TruffleDispatcher.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libnio.impl; + +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; +import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Inject; +import com.oracle.truffle.espresso.substitutions.JavaType; +import com.oracle.truffle.espresso.substitutions.Substitution; +import com.oracle.truffle.espresso.substitutions.Throws; + +@EspressoSubstitutions(type = "Lsun/nio/ch/TruffleDispatcher;", group = LibNio.class) +public final class Target_sun_nio_ch_TruffleDispatcher { + + @Substitution + @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); + } + + @Substitution + @Throws(IOException.class) + public static int readv0(@JavaType(FileDescriptor.class) StaticObject fd, long address, int len, @Inject TruffleIO io) { + ByteBuffer[] buffers = NativeUtils.getByteBuffersFromIOVec(address, len); + return Math.toIntExact(io.readByteBuffers(fd, FDAccess.forFileDescriptor(), buffers)); + + } + + @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); + } + + @Substitution + @Throws(IOException.class) + public static int 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 Math.toIntExact(io.writeByteBuffers(fd, FDAccess.forFileDescriptor(), buffers)); + } + + @Substitution + @Throws(IOException.class) + public static void close0(@JavaType(FileDescriptor.class) StaticObject fd, @Inject TruffleIO io) { + io.close(fd, FDAccess.forFileDescriptor()); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_fs_TruffleFilteredDirectoryStream.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_fs_TruffleFilteredDirectoryStream.java index ef37c29ee5bf..96b963ecbb17 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_fs_TruffleFilteredDirectoryStream.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnio/impl/Target_sun_nio_fs_TruffleFilteredDirectoryStream.java @@ -28,57 +28,116 @@ import java.nio.file.DirectoryStream; import java.util.Iterator; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.TruffleFile; +import com.oracle.truffle.espresso.impl.Klass; +import com.oracle.truffle.espresso.io.Throw; +import com.oracle.truffle.espresso.io.TruffleIO; +import com.oracle.truffle.espresso.libs.LibsMeta; +import com.oracle.truffle.espresso.libs.LibsState; import com.oracle.truffle.espresso.libs.libnio.LibNio; +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.JavaSubstitution; +import com.oracle.truffle.espresso.substitutions.Inject; import com.oracle.truffle.espresso.substitutions.JavaType; import com.oracle.truffle.espresso.substitutions.Substitution; import com.oracle.truffle.espresso.substitutions.Throws; -@EspressoSubstitutions(group = LibNio.class) +@EspressoSubstitutions(type = "Lsun/nio/fs/TruffleFilteredDirectoryStream;", group = LibNio.class) public final class Target_sun_nio_fs_TruffleFilteredDirectoryStream { @Substitution @Throws(IOException.class) @SuppressWarnings("unused") + @TruffleBoundary public static @JavaType(DirectoryStream.class) StaticObject directoryStream0( @JavaType(internalName = TRUFFLE_PATH) StaticObject dir, - @JavaType(Class.class) StaticObject directoryStreamClass) { - throw JavaSubstitution.unimplemented(); + @JavaType(Class.class) StaticObject directoryStreamClass, + @Inject LibsState libsState, + @Inject TruffleIO io, + @Inject EspressoContext context, + @Inject LibsMeta lMeta) { + TruffleFile tf = (TruffleFile) io.sun_nio_fs_TrufflePath_HIDDEN_TRUFFLE_FILE.getHiddenObject(dir); + try { + DirectoryStream hostStream = tf.newDirectoryStream(); + + Klass clazz = directoryStreamClass.getMirrorKlass(context.getMeta()); + @JavaType(internalName = "Lsun/nio/fs/TruffleFilteredDirectoryStream$ForeignDirectoryStream;") + StaticObject guestStream = clazz.allocateInstance(context); + lMeta.sun_nio_fs_TruffleFilteredDirectoryStream$ForeignDirectoryStream_init.invokeDirectSpecial( + /* this */ guestStream); + lMeta.sun_nio_fs_TruffleFilteredDirectoryStream$ForeignDirectoryStream_HIDDEN_HOST_REFERENCE.setHiddenObject(guestStream, hostStream); + return guestStream; + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } } @Substitution - @SuppressWarnings("unused") - public static boolean hasNext0(@JavaType(Iterator.class) StaticObject iterator) { - throw JavaSubstitution.unimplemented(); + @SuppressWarnings({"unused", "unchecked"}) + @TruffleBoundary + public static boolean hasNext0(@JavaType(Iterator.class) StaticObject iterator, @Inject LibsState libsState, @Inject EspressoContext ctx, @Inject LibsMeta lMeta) { + Iterator hostIterator = (Iterator) lMeta.sun_nio_fs_TruffleFilteredDirectoryStream$ForeignIterator_HIDDEN_HOST_REFERENCE.getHiddenObject(iterator); + if (hostIterator == null) { + throw Throw.throwIllegalArgumentException("iterator", ctx); + } + return hostIterator.hasNext(); } @Substitution - @SuppressWarnings("unused") - public static @JavaType(Object.class) StaticObject next0(@JavaType(Iterator.class) StaticObject iterator) { - throw JavaSubstitution.unimplemented(); + @SuppressWarnings({"unused", "unchecked"}) + @TruffleBoundary + public static @JavaType(String.class) StaticObject next0(@JavaType(Iterator.class) StaticObject iterator, @Inject LibsState libsState, + @Inject EspressoContext ctx, + @Inject LibsMeta lMeta) { + Iterator hostIterator = (Iterator) lMeta.sun_nio_fs_TruffleFilteredDirectoryStream$ForeignIterator_HIDDEN_HOST_REFERENCE.getHiddenObject(iterator); + if (hostIterator == null) { + throw Throw.throwIllegalArgumentException("iterator", ctx); + } + return ctx.getMeta().toGuestString(hostIterator.next().getName()); } @Substitution @Throws(IOException.class) - @SuppressWarnings("unused") - public static void close0(@JavaType(DirectoryStream.class) StaticObject directoryStream) { - throw JavaSubstitution.unimplemented(); + @SuppressWarnings({"unused", "unchecked"}) + @TruffleBoundary + public static void close0(@JavaType(DirectoryStream.class) StaticObject directoryStream, @Inject LibsState libsState, @Inject EspressoContext ctx, @Inject LibsMeta lMeta) { + DirectoryStream hostStream = (DirectoryStream) lMeta.sun_nio_fs_TruffleFilteredDirectoryStream$ForeignDirectoryStream_HIDDEN_HOST_REFERENCE.getHiddenObject( + directoryStream); + + if (hostStream == null) { + throw Throw.throwIllegalArgumentException("directoryStream", ctx); + } + try { + hostStream.close(); + } catch (IOException e) { + throw Throw.throwIOException(e, ctx); + } } @Substitution - @SuppressWarnings("unused") + @SuppressWarnings({"unused", "unchecked"}) + @TruffleBoundary public static @JavaType(Iterator.class) StaticObject iterator0( @JavaType(DirectoryStream.class) StaticObject directoryStream, - @JavaType(Class.class) StaticObject iteratorClass) { - throw JavaSubstitution.unimplemented(); + @JavaType(Class.class) StaticObject iteratorClass, + @Inject EspressoContext ctx, @Inject LibsState libsState, + @Inject LibsMeta lMeta) { + // retrieve host stream + DirectoryStream hostStream = (DirectoryStream) lMeta.sun_nio_fs_TruffleFilteredDirectoryStream$ForeignDirectoryStream_HIDDEN_HOST_REFERENCE.getHiddenObject( + directoryStream); + if (hostStream == null) { + throw Throw.throwIllegalArgumentException("directoryStream", ctx); + } + // allocate guest Iterator + Klass clazz = iteratorClass.getMirrorKlass(ctx.getMeta()); + @JavaType(internalName = "Lsun/nio/fs/TruffleFilteredDirectoryStream$ForeignIterator;") + StaticObject guestIterator = clazz.allocateInstance(ctx); + lMeta.sun_nio_fs_TruffleFilteredDirectoryStream$ForeignIterator_init.invokeDirectSpecial( + /* this */ guestIterator); + // link guest and host iterator + lMeta.sun_nio_fs_TruffleFilteredDirectoryStream$ForeignIterator_HIDDEN_HOST_REFERENCE.setHiddenObject(guestIterator, hostStream.iterator()); + return guestIterator; } - @Substitution - @SuppressWarnings("unused") - public static @JavaType(internalName = TRUFFLE_PATH) StaticObject toTrufflePath0( - @JavaType(Object.class) StaticObject truffleFile, - @JavaType(internalName = "Lsun/nio/fs/TruffleFileSystem;") StaticObject truffleFileSystem) { - throw JavaSubstitution.unimplemented(); - } } From 932d106097e55c07453422a5981138e77720be8b Mon Sep 17 00:00:00 2001 From: Elia Trachsel Date: Fri, 8 Aug 2025 15:32:40 +0200 Subject: [PATCH 09/26] Low-level Runtime substitutions: - Most of the substitutions just simply link against VM methods. - Introduction of JNU to store system encodings --- .../espresso/descriptors/EspressoSymbols.java | 10 + .../espresso/libs/InformationLeak.java | 15 ++ .../com/oracle/truffle/espresso/libs/JNU.java | 38 ++++ .../truffle/espresso/libs/LibsMeta.java | 35 ++++ .../libjava/impl/Target_java_io_Console.java | 48 +++++ .../impl/Target_java_lang_ClassLoader.java | 39 ++++ .../Target_java_lang_PhantomReference.java | 53 +++++ .../Target_java_lang_ProcessEnvironment.java | 61 ++++++ .../Target_java_lang_ProcessHandleImpl.java | 57 ++++++ ...rget_java_lang_ProcessHandleImpl_Info.java | 74 +++++++ .../impl/Target_java_lang_ProcessImpl.java | 187 ++++++++++++++++++ .../impl/Target_java_lang_Reference.java | 5 + .../Target_java_lang_SecurityManager.java | 51 +++++ .../impl/Target_java_lang_StackFrameInfo.java | 41 ++++ .../Target_java_lang_StackStreamFactory.java | 37 ++++ .../libjava/impl/Target_java_lang_System.java | 5 +- .../Target_java_lang_reflect_Executable.java | 55 ++++++ .../impl/Target_java_lang_reflect_Field.java | 43 ++++ ...t_jdk_internal_platform_CgroupMetrics.java | 36 ++++ .../espresso/runtime/EspressoContext.java | 7 + .../espresso/substitutions/VersionFilter.java | 12 ++ ...tackStreamFactory_AbstractStackWalker.java | 11 ++ .../Target_java_lang_VirtualThread.java | 6 + .../Target_java_lang_reflect_Array.java | 8 + .../oracle/truffle/espresso/vm/StackWalk.java | 17 +- 25 files changed, 941 insertions(+), 10 deletions(-) create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/JNU.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_io_Console.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_PhantomReference.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_ProcessEnvironment.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_ProcessHandleImpl.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_ProcessHandleImpl_Info.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_ProcessImpl.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_SecurityManager.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_StackFrameInfo.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_StackStreamFactory.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_reflect_Executable.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_reflect_Field.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_jdk_internal_platform_CgroupMetrics.java diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java index bf1349d8aa72..d8cfa5e2ad12 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java @@ -182,6 +182,8 @@ public static void ensureInitialized() { public static final Symbol sun_net_ConnectionResetException = SYMBOLS.putType("Lsun/net/ConnectionResetException;"); public static final Symbol java_net_UnknownHostException = SYMBOLS.putType("Ljava/net/UnknownHostException;"); public static final Symbol sun_nio_ch_IOStatus = SYMBOLS.putType("Lsun/nio/ch/IOStatus;"); + // libjava + public static final Symbol java_lang_ProcessHandleImpl$Info = SYMBOLS.putType("Ljava/lang/ProcessHandleImpl$Info;"); // libnio public static final Symbol sun_nio_fs_TruffleFilteredDirectoryStream$ForeignDirectoryStream = SYMBOLS.putType("Lsun/nio/fs/TruffleFilteredDirectoryStream$ForeignDirectoryStream;"); public static final Symbol sun_nio_fs_TruffleFilteredDirectoryStream$ForeignIterator = SYMBOLS.putType("Lsun/nio/fs/TruffleFilteredDirectoryStream$ForeignIterator;"); @@ -809,6 +811,14 @@ public static class Names { public static final Symbol UNSUPPORTED = SYMBOLS.putName("UNSUPPORTED"); public static final Symbol THROWN = SYMBOLS.putName("THROWN"); public static final Symbol UNSUPPORTED_CASE = SYMBOLS.putName("UNSUPPORTED_CASE"); + // java.lang.ProcessHandleImpl$Info + public static final Symbol command = SYMBOLS.putName("command"); + public static final Symbol commandLine = SYMBOLS.putName("commandLine"); + public static final Symbol arguments = SYMBOLS.putName("arguments"); + public static final Symbol startTime = SYMBOLS.putName("startTime"); + public static final Symbol totalTime = SYMBOLS.putName("totalTime"); + public static final Symbol user = SYMBOLS.putName("user"); + public static final Symbol initialized = SYMBOLS.putName("initialized"); // java.lang.invoke.* // CallSite public static final Symbol target = SYMBOLS.putName("target"); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/InformationLeak.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/InformationLeak.java index 2752af4b80df..dc9e4dced529 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/InformationLeak.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/InformationLeak.java @@ -33,6 +33,7 @@ import java.net.SocketException; import java.net.StandardSocketOptions; import java.nio.channels.ServerSocketChannel; +import java.util.Optional; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.espresso.io.Throw; @@ -50,6 +51,16 @@ public InformationLeak(EspressoContext ctx) { this.context = ctx; } + public long getPid() { + return ProcessHandle.current().pid(); + } + + @TruffleBoundary + public ProcessHandle.Info getProcessHandleInfo(long pid) { + Optional processHandle = ProcessHandle.of(pid); + return processHandle.map(ProcessHandle::info).orElse(null); + } + public void checkNetworkEnabled() { if (!context.getLanguage().enableNetworking()) { throw Throw.throwIllegalStateException("You are accessing deep LibNet classes even though networking is disabled", context); @@ -86,6 +97,10 @@ public boolean isReusePortAvailable0() { } } + public boolean istty() { + return context.getEnv().in() == System.in && context.getEnv().out() == System.out; + } + @TruffleBoundary public int isExclusvieBindAvailable() { try (ServerSocket socket1 = new ServerSocket()) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/JNU.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/JNU.java new file mode 100644 index 000000000000..6c9734502903 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/JNU.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs; + +import java.nio.charset.Charset; + +public class JNU { + private final String strEncoding = java.lang.System.getProperty("sun.jnu.encoding"); + private final Charset charSet = Charset.forName(strEncoding); + + public Charset getCharSet() { + return charSet; + } + + public String getString(byte[] arr, int index, int length) { + return new String(arr, index, length, charSet); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsMeta.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsMeta.java index fe0f83042c1e..44ecee84830e 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsMeta.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsMeta.java @@ -54,6 +54,22 @@ public final class LibsMeta implements ContextAccess { public final Field java_util_zip_Inflater_inputConsumed; public final Field java_util_zip_Inflater_outputConsumed; public final ObjectKlass java_util_zip_DataFormatException; + + // libjava + public final ObjectKlass java_lang_ProcessHandleImpl$Info; + public final Field java_lang_ProcessHandleImpl$Info_command; + public final Field java_lang_ProcessHandleImpl$Info_commandLine; + public final Field java_lang_ProcessHandleImpl$Info_arguments; + public final Field java_lang_ProcessHandleImpl$Info_startTime; + public final Field java_lang_ProcessHandleImpl$Info_totalTime; + public final Field java_lang_ProcessHandleImpl$Info_user; + public final ObjectKlass java_lang_SecurityManager; + public final Field java_lang_SecurityManager_initialized; + + // libnet + public final ObjectKlass java_net_NetworkInterface; + public final LibNetMeta net; + // Checkstyle: resume field name check @Override @@ -87,6 +103,25 @@ public LibsMeta(EspressoContext ctx) { java_util_zip_DataFormatException = knownKlass(EspressoSymbols.Types.java_util_zip_DataFormatException); java_util_zip_Inflater_inputConsumed = java_util_zip_Inflater.requireDeclaredField(EspressoSymbols.Names.inputConsumed, EspressoSymbols.Types._int); java_util_zip_Inflater_outputConsumed = java_util_zip_Inflater.requireDeclaredField(EspressoSymbols.Names.outputConsumed, EspressoSymbols.Types._int); + + // libjava + java_lang_ProcessHandleImpl$Info = knownKlass(EspressoSymbols.Types.java_lang_ProcessHandleImpl$Info); + java_lang_ProcessHandleImpl$Info_command = java_lang_ProcessHandleImpl$Info.requireDeclaredField(EspressoSymbols.Names.command, EspressoSymbols.Types.java_lang_String); + java_lang_ProcessHandleImpl$Info_commandLine = java_lang_ProcessHandleImpl$Info.requireDeclaredField(EspressoSymbols.Names.commandLine, EspressoSymbols.Types.java_lang_String); + java_lang_ProcessHandleImpl$Info_arguments = java_lang_ProcessHandleImpl$Info.requireDeclaredField(EspressoSymbols.Names.arguments, EspressoSymbols.Types.java_lang_String_array); + java_lang_ProcessHandleImpl$Info_startTime = java_lang_ProcessHandleImpl$Info.requireDeclaredField(EspressoSymbols.Names.startTime, EspressoSymbols.Types._long); + java_lang_ProcessHandleImpl$Info_totalTime = java_lang_ProcessHandleImpl$Info.requireDeclaredField(EspressoSymbols.Names.totalTime, EspressoSymbols.Types._long); + java_lang_ProcessHandleImpl$Info_user = java_lang_ProcessHandleImpl$Info.requireDeclaredField(EspressoSymbols.Names.user, EspressoSymbols.Types.java_lang_String); + java_lang_SecurityManager = knownKlass(EspressoSymbols.Types.java_lang_SecurityManager); + if (context.getJavaVersion().java25OrLater()) { + java_lang_SecurityManager_initialized = null; + } else { + java_lang_SecurityManager_initialized = java_lang_SecurityManager.requireDeclaredField(EspressoSymbols.Names.initialized, EspressoSymbols.Types._boolean); + } + + // libnet + java_net_NetworkInterface = knownKlass(EspressoSymbols.Types.java_net_NetworkInterface); + this.net = context.getLanguage().enableNetworking() ? new LibNetMeta() : null; } public ObjectKlass knownKlass(Symbol type) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_io_Console.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_io_Console.java new file mode 100644 index 000000000000..4659f02a976e --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_io_Console.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libjava.impl; + +import java.io.Console; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.espresso.libs.InformationLeak; +import com.oracle.truffle.espresso.libs.libjava.LibJava; +import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Inject; +import com.oracle.truffle.espresso.substitutions.JavaType; +import com.oracle.truffle.espresso.substitutions.Substitution; + +@EspressoSubstitutions(value = Console.class, group = LibJava.class) +public final class Target_java_io_Console { + @Substitution + @TruffleBoundary + public static boolean istty(@Inject InformationLeak iL) { + return iL.istty(); + } + + @Substitution + public static @JavaType(String.class) StaticObject encoding() { + return StaticObject.NULL; + } +} 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 3f3d50b256f9..686e820f515e 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,12 +22,16 @@ */ package com.oracle.truffle.espresso.libs.libjava.impl; +import java.nio.ByteBuffer; import java.security.ProtectionDomain; 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.classfile.descriptors.Symbol; import com.oracle.truffle.espresso.classfile.descriptors.Type; +import com.oracle.truffle.espresso.ffi.Pointer; +import com.oracle.truffle.espresso.ffi.nfi.NativeUtils; import com.oracle.truffle.espresso.libs.libjava.LibJava; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; @@ -135,6 +139,41 @@ public static void registerNatives() { return ctx.getVM().defineClass(type, loader, pd, buf); } + @Substitution + @TruffleBoundary + public static @JavaType(Class.class) StaticObject defineClass2(@JavaType(ClassLoader.class) StaticObject loader, @JavaType(String.class) StaticObject name, + @JavaType(ByteBuffer.class) StaticObject data, int off, int len, + @JavaType(ProtectionDomain.class) StaticObject pd, + // TODO: source unused + @SuppressWarnings("unused") @JavaType(String.class) StaticObject source, + @Inject EspressoContext ctx) { + if (StaticObject.isNull(data)) { + throw ctx.getMeta().throwNullPointerException(); + } + if (len < 0) { + throw ctx.getMeta().throwArrayIndexOutOfBounds(len); + } + if (off < 0) { + throw ctx.getMeta().throwArrayIndexOutOfBounds(off); + } + // retrieve the GuestBuffer as Array + @Pointer + TruffleObject dataAddrPointer = ctx.getJNI().GetDirectBufferAddress(data); + ByteBuffer dataBuffer = NativeUtils.directByteBuffer(dataAddrPointer, len + off); + + 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); + + } + private static String toSlashName(String name) { return name.replace('.', '/'); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_PhantomReference.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_PhantomReference.java new file mode 100644 index 000000000000..eea67a50280e --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_PhantomReference.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libjava.impl; + +import java.lang.ref.PhantomReference; +import java.lang.ref.Reference; + +import com.oracle.truffle.espresso.libs.libjava.LibJava; +import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Inject; +import com.oracle.truffle.espresso.substitutions.JavaType; +import com.oracle.truffle.espresso.substitutions.Substitution; +import com.oracle.truffle.espresso.substitutions.SubstitutionProfiler; +import com.oracle.truffle.espresso.substitutions.VersionFilter; +import com.oracle.truffle.espresso.vm.VM; + +@EspressoSubstitutions(value = PhantomReference.class, group = LibJava.class) +public final class Target_java_lang_PhantomReference { + @Substitution(hasReceiver = true, languageFilter = VersionFilter.Java25OrLater.class) + public static void clear0(@JavaType(Reference.class) StaticObject ref, + @Inject SubstitutionProfiler profiler, @Inject VM vm) { + vm.JVM_ReferenceClear(ref, profiler); + + } + + @Substitution(hasReceiver = true) + public static boolean refersTo0(@JavaType(Reference.class) StaticObject ref, @JavaType(Object.class) StaticObject object, + @Inject SubstitutionProfiler profiler, @Inject VM vm) { + return vm.JVM_PhantomReferenceRefersTo(ref, object, profiler); + } + +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_ProcessEnvironment.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_ProcessEnvironment.java new file mode 100644 index 000000000000..85646841b28b --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_ProcessEnvironment.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libjava.impl; + +import java.nio.charset.Charset; +import java.util.Map; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.espresso.libs.JNU; +import com.oracle.truffle.espresso.libs.libjava.LibJava; +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; +import com.oracle.truffle.espresso.substitutions.JavaType; +import com.oracle.truffle.espresso.substitutions.Substitution; + +@EspressoSubstitutions(type = "Ljava/lang/ProcessEnvironment;", group = LibJava.class) +public final class Target_java_lang_ProcessEnvironment { + @Substitution + @TruffleBoundary + public static @JavaType(byte[][].class) StaticObject environ(@Inject EspressoContext ctx, @Inject JNU jnu) { + Charset charSet = jnu.getCharSet(); + Map truffleEnvironment = ctx.getEnv().getEnvironment(); + int size = truffleEnvironment.size(); + StaticObject[] environ = new StaticObject[size * 2]; + + int i = 0; + for (Map.Entry entry : truffleEnvironment.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + byte[] keyBytes = key.getBytes(charSet); + byte[] valueBytes = value.getBytes(charSet); + environ[i * 2] = ctx.getAllocator().wrapArrayAs(ctx.getMeta()._byte_array, keyBytes); + environ[i * 2 + 1] = ctx.getAllocator().wrapArrayAs(ctx.getMeta()._byte_array, valueBytes); + i++; + } + + return ctx.getAllocator().wrapArrayAs(ctx.getMeta()._byte_array.array(), environ); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_ProcessHandleImpl.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_ProcessHandleImpl.java new file mode 100644 index 000000000000..e61708214703 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_ProcessHandleImpl.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libjava.impl; + +import java.time.Instant; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.espresso.libs.InformationLeak; +import com.oracle.truffle.espresso.libs.libjava.LibJava; +import com.oracle.truffle.espresso.runtime.EspressoContext; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Inject; +import com.oracle.truffle.espresso.substitutions.Substitution; + +@EspressoSubstitutions(type = "Ljava/lang/ProcessHandleImpl;", group = LibJava.class) +public final class Target_java_lang_ProcessHandleImpl { + @Substitution + public static void initNative() { + // nop + } + + @Substitution + public static long getCurrentPid0(@Inject EspressoContext ctx) { + return ctx.getInformationLeak().getPid(); + } + + @Substitution + @TruffleBoundary + public static long isAlive0(long pid, @Inject InformationLeak iL) { + ProcessHandle.Info info = iL.getProcessHandleInfo(pid); + if (info != null) { + return info.startInstant().map(Instant::toEpochMilli).orElse(-1L); + } else { + return -1; + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_ProcessHandleImpl_Info.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_ProcessHandleImpl_Info.java new file mode 100644 index 000000000000..557b8f904847 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_ProcessHandleImpl_Info.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libjava.impl; + +import java.time.Duration; +import java.time.Instant; + +import com.oracle.truffle.espresso.libs.InformationLeak; +import com.oracle.truffle.espresso.libs.LibsMeta; +import com.oracle.truffle.espresso.libs.libjava.LibJava; +import com.oracle.truffle.espresso.meta.Meta; +import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Inject; +import com.oracle.truffle.espresso.substitutions.JavaType; +import com.oracle.truffle.espresso.substitutions.Substitution; + +@EspressoSubstitutions(type = "Ljava/lang/ProcessHandleImpl$Info;", group = LibJava.class) +public final class Target_java_lang_ProcessHandleImpl_Info { + @Substitution + public static void initIDs() { + // nop + } + + @Substitution(hasReceiver = true) + public static void info0(@JavaType(internalName = "Ljava/lang/ProcessHandleImpl$Info;") StaticObject self, long pid, @Inject InformationLeak iL, @Inject LibsMeta libsMeta) { + ProcessHandle.Info info = iL.getProcessHandleInfo(pid); + if (info != null) { + Meta meta = libsMeta.getMeta(); + String command = info.command().orElse(""); + libsMeta.java_lang_ProcessHandleImpl$Info_command.setObject(self, meta.toGuestString(command)); + + String commandLine = info.commandLine().orElse(""); + libsMeta.java_lang_ProcessHandleImpl$Info_commandLine.setObject(self, meta.toGuestString(commandLine)); + + String[] arguments = info.arguments().orElse(new String[0]); + StaticObject[] arr = new StaticObject[arguments.length]; + for (int i = 0; i < arguments.length; i++) { + arr[i] = meta.toGuestString(arguments[i]); + } + libsMeta.java_lang_ProcessHandleImpl$Info_arguments.setObject(self, libsMeta.getContext().getAllocator().wrapArrayAs(meta.java_lang_String_array, arr)); + + long startTime = info.startInstant().orElse(Instant.EPOCH).toEpochMilli(); + libsMeta.java_lang_ProcessHandleImpl$Info_startTime.setLong(self, startTime); + + long totalTime = info.totalCpuDuration().orElse(Duration.ZERO).toMillis(); + libsMeta.java_lang_ProcessHandleImpl$Info_totalTime.setLong(self, totalTime); + + String user = info.user().orElse(""); + libsMeta.java_lang_ProcessHandleImpl$Info_user.setObject(self, meta.toGuestString(user)); + } + } + +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_ProcessImpl.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_ProcessImpl.java new file mode 100644 index 000000000000..1e8d842ea5e1 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_ProcessImpl.java @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libjava.impl; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.graalvm.polyglot.io.ProcessHandler; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.TruffleFile; +import com.oracle.truffle.api.io.TruffleProcessBuilder; +import com.oracle.truffle.espresso.EspressoLanguage; +import com.oracle.truffle.espresso.io.Throw; +import com.oracle.truffle.espresso.io.TruffleIO; +import com.oracle.truffle.espresso.libs.JNU; +import com.oracle.truffle.espresso.libs.libjava.LibJava; +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; +import com.oracle.truffle.espresso.substitutions.JavaSubstitution; +import com.oracle.truffle.espresso.substitutions.JavaType; +import com.oracle.truffle.espresso.substitutions.Substitution; +import com.oracle.truffle.espresso.substitutions.Throws; + +@EspressoSubstitutions(type = "Ljava/lang/ProcessImpl;", group = LibJava.class) +public final class Target_java_lang_ProcessImpl { + @Substitution(hasReceiver = true) + @TruffleBoundary + @Throws(IOException.class) + @SuppressWarnings("unused") + public static int forkAndExec(int mode, @JavaType(byte[].class) StaticObject helperpath, + @JavaType(byte[].class) StaticObject prog, + @JavaType(byte[].class) StaticObject argBlock, int argc, + @JavaType(byte[].class) StaticObject envBlock, int envc, + @JavaType(byte[].class) StaticObject dir, + @JavaType(int[].class) StaticObject fds, + boolean redirectErrorStream, + @Inject EspressoContext ctx, + @Inject EspressoLanguage lang, + @Inject TruffleIO io, + @Inject JNU jnu) { + /* + * In the end the fds array should hold the FileDescriptor to channels with which the parent + * can access the input, output and error stream of the newly created process. In some cases + * the parent already provides the fds. + */ + + // unwrap everything + @SuppressWarnings("unused") + byte[] helperpathArr = helperpath.unwrap(lang); + byte[] progArr = prog.unwrap(lang); + byte[] argBlockArr = argBlock.unwrap(lang); + byte[] envBlockArr = envBlock.unwrap(lang); + byte[] dirArr = dir.unwrap(lang); + byte[] fdsArr = fds.unwrap(lang); + String[] command = new String[argc + 1]; + + // set the command string array + command[0] = jnu.getString(progArr, 0, progArr.length - 1); + decodeCmdarray(argBlockArr, argc, command, jnu); + TruffleProcessBuilder builder = ctx.getEnv().newProcessBuilder(command); + + // set environment + Map environment = decodeEnv(envBlockArr, envc, ctx, jnu); + builder.environment(environment); + + // set directory + String dirString = jnu.getString(dirArr, 0, dirArr.length - 1); + TruffleFile dirTF = io.getPublicTruffleFileSafe(dirString); + builder.directory(dirTF); + + // set fds + if (fdsArr[0] == 0) { + // we trust truffle to do the correct redirection from the parent to the child + builder.redirectInput(ProcessHandler.Redirect.INHERIT); + fdsArr[0] = -1; + } else if (fdsArr[0] == -1) { + /* + * Here we would need to create a new stream and set it as standardInput with + * builder.redirectInput. This is left unimplemented for the moment since it was not + * used. + */ + throw JavaSubstitution.unimplemented(); + + } else { + /* + * In this case, we need to retrieve the channel associated with the fd. Then we need to + * cast it somehow to an OutputStream and use createRedirectToStream. This is left + * unimplemented for the moment since it was not used. + */ + throw JavaSubstitution.unimplemented(); + } + + if (fdsArr[1] == 1) { + builder.redirectOutput(ProcessHandler.Redirect.INHERIT); + fdsArr[1] = -1; + } else { + throw JavaSubstitution.unimplemented(); + } + builder.redirectErrorStream(redirectErrorStream); + if (fdsArr[2] == 2) { + builder.redirectError(ProcessHandler.Redirect.INHERIT); + fdsArr[2] = -1; + } else { + throw JavaSubstitution.unimplemented(); + } + Process p; + try { + p = builder.start(); + } catch (IOException e) { + throw Throw.throwIOException(e, ctx); + } + return Math.toIntExact(p.pid()); + } + + @Substitution + public static void init() { + // nop + } + + private static void decodeCmdarray(byte[] argBlock, int argc, String[] command, JNU jnu) { + int i = 0; + int argIndex = 0; + while (i < argBlock.length && argIndex < argc) { + int start = i; + + while (i < argBlock.length && argBlock[i] != 0) { + i++; + } + + if (start < i) { + String arg = jnu.getString(argBlock, start, i - start); + command[argIndex + 1] = arg; // +1 because args[0] is the program path + argIndex++; + } + i++; // skip the null-terminator + } + } + + private static Map decodeEnv(byte[] envBlockArr, int envc, EspressoContext ctx, JNU jnu) { + Map envMap = new HashMap<>(); + int i = 0; + for (int j = 0; j < envc; j++) { + int start = i; + + while (i < envBlockArr.length && envBlockArr[i] != 0) { + i++; + } + + String envStr = jnu.getString(envBlockArr, start, i - start); + int equalsIndex = envStr.indexOf('='); + if (equalsIndex != -1) { + String key = envStr.substring(0, equalsIndex); + String value = envStr.substring(equalsIndex + 1); + envMap.put(key, value); + } else { + throw Throw.throwIOException("The byte array encoding for the Enviornment Map Entry didnt include =", ctx); + } + i++; // skip the null-terminator + } + return envMap; + } + +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_Reference.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_Reference.java index 8500907ead88..fbeadfa0cd3e 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_Reference.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_Reference.java @@ -53,4 +53,9 @@ public static boolean refersTo0(@JavaType(Reference.class) StaticObject self, @J public static @JavaType(Reference.class) StaticObject getAndClearReferencePendingList(@Inject EspressoContext ctx) { return ctx.getVM().JVM_GetAndClearReferencePendingList(); } + + @Substitution(hasReceiver = true) + public static void clear0(@JavaType(Reference.class) StaticObject self, @Inject VM vm, @Inject SubstitutionProfiler profiler) { + vm.JVM_ReferenceClear(self, profiler); + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_SecurityManager.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_SecurityManager.java new file mode 100644 index 000000000000..f637f17781f9 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_SecurityManager.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libjava.impl; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.espresso.io.Throw; +import com.oracle.truffle.espresso.libs.LibsMeta; +import com.oracle.truffle.espresso.libs.libjava.LibJava; +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; +import com.oracle.truffle.espresso.substitutions.JavaType; +import com.oracle.truffle.espresso.substitutions.Substitution; +import com.oracle.truffle.espresso.substitutions.VersionFilter; +import com.oracle.truffle.espresso.vm.VM; + +@SuppressWarnings("deprecated") +@EspressoSubstitutions(value = SecurityManager.class, group = LibJava.class) +public final class Target_java_lang_SecurityManager { + + @TruffleBoundary + @SuppressWarnings("deprecated") + @Substitution(hasReceiver = true, languageFilter = VersionFilter.Java21.class) + public static @JavaType(Class[].class) StaticObject getClassContext(@JavaType(SecurityManager.class) StaticObject self, @Inject VM vm, @Inject LibsMeta libsMeta, @Inject EspressoContext context) { + if (!libsMeta.java_lang_SecurityManager_initialized.getBoolean(self)) { + throw Throw.throwSecurityException("security manager not initialized", context); + } + return vm.JVM_GetClassContext(); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_StackFrameInfo.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_StackFrameInfo.java new file mode 100644 index 000000000000..4c22c5d439bd --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_StackFrameInfo.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libjava.impl; + +import com.oracle.truffle.espresso.libs.libjava.LibJava; +import com.oracle.truffle.espresso.meta.Meta; +import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Inject; +import com.oracle.truffle.espresso.substitutions.JavaType; +import com.oracle.truffle.espresso.substitutions.Substitution; +import com.oracle.truffle.espresso.substitutions.VersionFilter; +import com.oracle.truffle.espresso.vm.VM; + +@EspressoSubstitutions(type = "Ljava/lang/StackFrameInfo;", group = LibJava.class) +public final class Target_java_lang_StackFrameInfo { + @Substitution(hasReceiver = true, languageFilter = VersionFilter.Java25OrLater.class) + public static void expandStackFrameInfo(@JavaType(internalName = "Ljava/lang/StackFrameInfo;") StaticObject obj, @Inject Meta meta) { + VM.JVM_ExpandStackFrameInfo(obj, meta); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_StackStreamFactory.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_StackStreamFactory.java new file mode 100644 index 000000000000..3b7d53270774 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_StackStreamFactory.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libjava.impl; + +import com.oracle.truffle.espresso.meta.Meta; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Inject; +import com.oracle.truffle.espresso.substitutions.Substitution; +import com.oracle.truffle.espresso.vm.StackWalk; + +@EspressoSubstitutions(type = "Ljava/lang/StackStreamFactory;") +public final class Target_java_lang_StackStreamFactory { + @Substitution + public static boolean checkStackWalkModes(@Inject Meta meta) { + return StackWalk.synchronizedConstants(meta); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_System.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_System.java index a80fc59bad32..913161bccb01 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_System.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_System.java @@ -30,6 +30,7 @@ import com.oracle.truffle.espresso.descriptors.EspressoSymbols.Types; import com.oracle.truffle.espresso.impl.ObjectKlass; import com.oracle.truffle.espresso.io.TruffleIO; +import com.oracle.truffle.espresso.libs.JNU; import com.oracle.truffle.espresso.libs.libjava.LibJava; import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.runtime.EspressoContext; @@ -78,7 +79,7 @@ public static final class Raw { @Substitution @TruffleBoundary - public static @JavaType(String[].class) StaticObject platformProperties(@Inject EspressoContext ctx, @Inject TruffleIO io) { + public static @JavaType(String[].class) StaticObject platformProperties(@Inject EspressoContext ctx, @Inject TruffleIO io, @Inject JNU jnu) { // Import properties from host. Props props = new Props(ctx); String[] known = new String[props.fixedLength]; @@ -86,7 +87,7 @@ public static final class Raw { known[props.userDirNdx] = java.lang.System.getProperty("user.dir"); known[props.userNameNdx] = java.lang.System.getProperty("user.name"); - known[props.sunJnuEncodingNdx] = java.lang.System.getProperty("sun.jnu.encoding"); + known[props.sunJnuEncodingNdx] = jnu.getCharSet().toString(); if (ctx.getJavaVersion().java21OrEarlier()) { known[props.fileEncodingNdx] = java.lang.System.getProperty("file.encoding"); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_reflect_Executable.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_reflect_Executable.java new file mode 100644 index 000000000000..a566e2ef8206 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_reflect_Executable.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libjava.impl; + +import java.lang.reflect.Executable; +import java.lang.reflect.Parameter; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.espresso.EspressoLanguage; +import com.oracle.truffle.espresso.libs.libjava.LibJava; +import com.oracle.truffle.espresso.meta.Meta; +import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Inject; +import com.oracle.truffle.espresso.substitutions.JavaType; +import com.oracle.truffle.espresso.substitutions.Substitution; +import com.oracle.truffle.espresso.substitutions.SubstitutionProfiler; +import com.oracle.truffle.espresso.vm.VM; + +@EspressoSubstitutions(value = Executable.class, group = LibJava.class) +public final class Target_java_lang_reflect_Executable { + @Substitution(hasReceiver = true) + public static @JavaType(Parameter[].class) StaticObject getParameters0(@JavaType(Executable.class) StaticObject self, + @Inject VM vm, @Inject EspressoLanguage language, + @Inject Meta meta, + @Inject SubstitutionProfiler profiler) { + return vm.JVM_GetMethodParameters(self, language, meta, profiler); + } + + @TruffleBoundary + @Substitution(hasReceiver = true) + public static @JavaType(byte[].class) StaticObject getTypeAnnotationBytes0(@JavaType(Executable.class) StaticObject self, @Inject VM vm) { + return vm.JVM_GetMethodTypeAnnotations(self); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_reflect_Field.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_reflect_Field.java new file mode 100644 index 000000000000..68d96f5a5da6 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_reflect_Field.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libjava.impl; + +import java.lang.reflect.Field; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.espresso.libs.libjava.LibJava; +import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Inject; +import com.oracle.truffle.espresso.substitutions.JavaType; +import com.oracle.truffle.espresso.substitutions.Substitution; +import com.oracle.truffle.espresso.vm.VM; + +@EspressoSubstitutions(value = Field.class, group = LibJava.class) +public final class Target_java_lang_reflect_Field { + @TruffleBoundary + @Substitution(hasReceiver = true) + public static @JavaType(byte[].class) StaticObject getTypeAnnotationBytes0(@JavaType(Field.class) StaticObject self, @Inject VM vm) { + return vm.JVM_GetFieldTypeAnnotations(self); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_jdk_internal_platform_CgroupMetrics.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_jdk_internal_platform_CgroupMetrics.java new file mode 100644 index 000000000000..43afbe7e3332 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_jdk_internal_platform_CgroupMetrics.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libjava.impl; + +import com.oracle.truffle.espresso.libs.libjava.LibJava; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Substitution; + +@EspressoSubstitutions(type = "Ljdk/internal/platform/CgroupMetrics;", group = LibJava.class) +public final class Target_jdk_internal_platform_CgroupMetrics { + + @Substitution + public static boolean isUseContainerSupport() { + return false; + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java index 5fed0da3ac38..3663411567b6 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java @@ -100,6 +100,7 @@ import com.oracle.truffle.espresso.jni.JNIHandles; import com.oracle.truffle.espresso.jni.JniEnv; import com.oracle.truffle.espresso.libs.InformationLeak; +import com.oracle.truffle.espresso.libs.JNU; import com.oracle.truffle.espresso.libs.LibsMeta; import com.oracle.truffle.espresso.libs.LibsState; import com.oracle.truffle.espresso.meta.EspressoError; @@ -203,6 +204,7 @@ public final class EspressoContext implements RuntimeAccess Date: Tue, 17 Dec 2024 19:18:49 +0100 Subject: [PATCH 10/26] Add NativeMemory abstraction to Espresso. --- .../MemorySegmentChunkedMemoryImpl.java | 243 ++++++++ .../espresso/memory/SafeMemoryAccess.java | 79 +++ .../espresso/memory/UnsafeMemoryImpl.java | 52 ++ .../truffle/espresso/ffi/NativeAccess.java | 37 +- .../memory/ByteArrayChunkedMemoryImpl.java | 172 ++++++ .../ffi/memory/ChunkedNativeMemory.java | 94 +++ .../espresso/ffi/memory/NativeMemory.java | 84 +++ .../ffi/memory/ProtoChunkedMemoryImpl.java | 244 ++++++++ .../ffi/memory/UnsafeNativeMemory.java | 167 ++++++ .../ffi/nfi/NFIIsolatedNativeAccess.java | 103 ++-- .../espresso/ffi/nfi/NFINativeAccess.java | 14 + .../standard/Target_sun_misc_Unsafe.java | 537 +++++++++++++----- 12 files changed, 1613 insertions(+), 213 deletions(-) create mode 100644 espresso/src/com.oracle.truffle.espresso.memory/src/com/oracle/truffle/espresso/memory/MemorySegmentChunkedMemoryImpl.java create mode 100644 espresso/src/com.oracle.truffle.espresso.memory/src/com/oracle/truffle/espresso/memory/SafeMemoryAccess.java create mode 100644 espresso/src/com.oracle.truffle.espresso.memory/src/com/oracle/truffle/espresso/memory/UnsafeMemoryImpl.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ByteArrayChunkedMemoryImpl.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ChunkedNativeMemory.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/NativeMemory.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ProtoChunkedMemoryImpl.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/UnsafeNativeMemory.java diff --git a/espresso/src/com.oracle.truffle.espresso.memory/src/com/oracle/truffle/espresso/memory/MemorySegmentChunkedMemoryImpl.java b/espresso/src/com.oracle.truffle.espresso.memory/src/com/oracle/truffle/espresso/memory/MemorySegmentChunkedMemoryImpl.java new file mode 100644 index 000000000000..7d095be14ea4 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.memory/src/com/oracle/truffle/espresso/memory/MemorySegmentChunkedMemoryImpl.java @@ -0,0 +1,243 @@ +package com.oracle.truffle.espresso.memory; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; +import java.lang.invoke.VarHandle; + +public class MemorySegmentChunkedMemoryImpl implements 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; + + public Arena getArena() { + return arena; + } + + protected MemorySegmentChunkedMemoryImpl(Arena arena) { + this.arena = arena; + } + + @Override + public void setMemory(long address, long bytes, byte value) { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + chunk.asSlice(chunkOffset, bytes).fill(value); + } + + @Override + public void putBoolean(long address, boolean value, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + 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 + public void putByte(long address, byte value, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + 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 + public void putChar(long address, char value, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + 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 + public void putShort(long address, short value, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + 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 + public void putInt(long address, int value, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + 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 + public void putFloat(long address, float value, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + 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 + public void putDouble(long address, double value, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + 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 + public void putLong(long address, long value, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + 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 + public boolean getBoolean(long address, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + 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 + public byte getByte(long address, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + 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 + public char getChar(long address, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + 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 + public short getShort(long address, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + 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 + public int getInt(long address, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + 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 + public float getFloat(long address, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + 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 + public double getDouble(long address, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + 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 + public long getLong(long address, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + MemorySegment chunk = getChunk(address); + 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 + protected MemorySegment allocateChunk(long bytes) { + // At least 8 bytes to ensure aligned accesses work. + return arena.allocate(bytes, Long.BYTES); + } + + @Override + protected long getChunkSize(long address) { + return getChunk(address).byteSize(); + } + + @Override + protected void copyBytes(long fromAddress, long toAddress, long byteSize) { + MemorySegment.copy(getChunk(fromAddress), getChunkOffset(fromAddress), getChunk(toAddress), getChunkOffset(toAddress), byteSize); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.memory/src/com/oracle/truffle/espresso/memory/SafeMemoryAccess.java b/espresso/src/com.oracle.truffle.espresso.memory/src/com/oracle/truffle/espresso/memory/SafeMemoryAccess.java new file mode 100644 index 000000000000..7acc82d2dc62 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.memory/src/com/oracle/truffle/espresso/memory/SafeMemoryAccess.java @@ -0,0 +1,79 @@ +package com.oracle.truffle.espresso.memory; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.util.ArrayList; +import java.util.concurrent.ConcurrentLinkedQueue; + +final class SafeMemoryAccess extends ChunkedNativeMemory { + + private static final int OFFSET_BITS = 38; // 256GB max buffer size + private static final int CHUNK_BITS = Long.SIZE - OFFSET_BITS; // 64M chunks + private static final long OFFSET_MASK = (1L << OFFSET_BITS) - 1; + + private static int getChunkIndex(long address) { + return (int) (address >>> OFFSET_BITS); + } + + private final ArrayList chunks = new ArrayList<>(); + private final ConcurrentLinkedQueue freeList = new ConcurrentLinkedQueue<>(); + + public SafeMemoryAccess() { + chunks.add(MemorySegment.NULL); // NULL sentinel + } + + @Override + public long reallocateMemory(long address, long bytes) { + int oldChunkIndex = getChunkIndex(address); + long newAddress = allocateMemory(bytes); + int newChunkIndex = getChunkIndex(newAddress); + MemorySegment oldChunk = chunks.get(oldChunkIndex); + MemorySegment newChunk = chunks.get(newChunkIndex); + MemorySegment.copy(oldChunk, 0, newChunk, 0, Math.min(oldChunk.byteSize(), newChunk.byteSize())); + freeMemory(address); + return newAddress; + } + + @Override + public long allocateMemory(long bytes) { + Integer chunkIndex = null; + while ((chunkIndex = freeList.poll()) == null) { + synchronized (chunks) { + chunks.add(null); + freeList.add(chunks.size() - 1); + } + } + MemorySegment chunk = allocateChunk(bytes); + chunks.set(chunkIndex, chunk); + return encodeAddress(chunkIndex, 0); + } + + private static MemorySegment allocateChunk(long bytes) { + return Arena.ofAuto().allocate(bytes); + } + + @Override + public void freeMemory(long address) { + assert getChunkOffset(address) == 0 : "invalid address"; + int chunkIndex = getChunkIndex(address); + chunks.set(chunkIndex, null); + freeList.add(chunkIndex); + } + + @Override + protected MemorySegment getChunk(long address) { + int chunkIndex = getChunkIndex(address); + return chunks.get(chunkIndex); + } + + @Override + protected long getChunkOffset(long address) { + return address & OFFSET_MASK; + } + + private 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; + } +} diff --git a/espresso/src/com.oracle.truffle.espresso.memory/src/com/oracle/truffle/espresso/memory/UnsafeMemoryImpl.java b/espresso/src/com.oracle.truffle.espresso.memory/src/com/oracle/truffle/espresso/memory/UnsafeMemoryImpl.java new file mode 100644 index 000000000000..b9fb6d795b17 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso.memory/src/com/oracle/truffle/espresso/memory/UnsafeMemoryImpl.java @@ -0,0 +1,52 @@ +package com.oracle.truffle.espresso.memory; + +import com.oracle.truffle.espresso.vm.UnsafeAccess; +import sun.misc.Unsafe; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; + +public class UnsafeMemoryImpl extends MemorySegmentChunkedMemoryImpl { + + private static final MemorySegment ALL = MemorySegment.ofAddress(0).reinterpret(Long.MAX_VALUE); + private static final Unsafe UNSAFE = UnsafeAccess.get(); + + protected UnsafeMemoryImpl(Arena arena) { + super(arena); + } + + @Override + public long reallocateMemory(long address, long bytes) { + return UNSAFE.reallocateMemory(address, bytes); + } + + @Override + public long allocateMemory(long bytes) { + return UNSAFE.allocateMemory(bytes); + } + + @Override + public void freeMemory(long address) { + UNSAFE.freeMemory(address); + } + + @Override + protected MemorySegment getChunk(long address) { + return ALL; + } + + @Override + protected long getChunkOffset(long address) { + return address; + } + + @Override + protected MemorySegment allocateChunk(long bytes) { + throw new UnsupportedOperationException(); + } + + @Override + protected void copyBytes(long fromAddress, long toAddress, long byteSize) { + throw new UnsupportedOperationException(); + } +} 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..2866a2f994d7 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 @@ -26,6 +26,7 @@ import java.nio.file.Paths; import java.util.List; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; import org.graalvm.options.OptionValues; import com.oracle.truffle.api.CompilerDirectives; @@ -219,8 +220,13 @@ default boolean isBuiltIn(@SuppressWarnings("unused") String libname) { * {@link InteropLibrary#hasBufferElements(Object) buffer}. * @throws IllegalArgumentException if the size is negative */ - @Buffer - TruffleObject allocateMemory(long size); + default @Buffer TruffleObject allocateMemory(long size) { + long address = nativeMemory().allocateMemory(size); + if (address == 0) { + return null; + } + return RawPointer.create(address); + } /** * Similar to realloc. The result of allocating a 0-sized buffer is an implementation detail. @@ -229,13 +235,32 @@ default boolean isBuiltIn(@SuppressWarnings("unused") String libname) { * {@link InteropLibrary#hasBufferElements(Object) buffer}. * @throws IllegalArgumentException if the size is negative */ - @Buffer - TruffleObject reallocateMemory(@Pointer TruffleObject buffer, long newSize); + default @Buffer TruffleObject reallocateMemory(@Pointer TruffleObject buffer, long newSize) { + long address = 0; + try { + address = InteropLibrary.getUncached().asPointer(buffer); + } catch (UnsupportedMessageException e) { + throw EspressoError.shouldNotReachHere(e); + } + long rawPointer = nativeMemory().reallocateMemory(address, newSize); + if (rawPointer == 0) { + return null; + } + return RawPointer.create(rawPointer); + } /** * Similar to free. Accessing the buffer after free may cause explosive undefined behavior. */ - void freeMemory(@Pointer TruffleObject buffer); + default void freeMemory(@Pointer TruffleObject buffer) { + long address = 0; + try { + address = InteropLibrary.getUncached().asPointer(buffer); + } catch (UnsupportedMessageException e) { + throw EspressoError.shouldNotReachHere(e); + } + nativeMemory().freeMemory(address); + } /** * Sinking, make a Java method accessible to the native world. Returns an @@ -292,4 +317,6 @@ interface Provider { NativeAccess create(TruffleLanguage.Env env); } + + NativeMemory nativeMemory(); } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ByteArrayChunkedMemoryImpl.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ByteArrayChunkedMemoryImpl.java new file mode 100644 index 000000000000..30678d7b61ec --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ByteArrayChunkedMemoryImpl.java @@ -0,0 +1,172 @@ +package com.oracle.truffle.espresso.ffi.memory; + +import java.util.Arrays; + +import com.oracle.truffle.espresso.vm.UnsafeAccess; + +import sun.misc.Unsafe; + +public class ByteArrayChunkedMemoryImpl extends ChunkedNativeMemory { + + private static final Unsafe UNSAFE = UnsafeAccess.get(); + + private static void validateAccess(int length, int byteIndex, int accessByteSize) { + if (byteIndex < 0 || byteIndex > length - accessByteSize) { + throw new IndexOutOfBoundsException(); + } + } + + @Override + public void setMemory(long address, long bytes, byte value) { + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(bytes), Byte.BYTES); + Arrays.fill(chunk, 0, Math.toIntExact(bytes), value); + } + + @Override + public void putByte(long address, byte value, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(chunkOffset), Byte.BYTES); + 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 putShort(long address, short value, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(chunkOffset), Short.BYTES); + 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 putInt(long address, int value, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(chunkOffset), Integer.BYTES); + 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 byte getByte(long address, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(chunkOffset), Byte.BYTES); + 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 getShort(long address, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(chunkOffset), Short.BYTES); + 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 getInt(long address, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(chunkOffset), Integer.BYTES); + 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 void putLong(long address, long value, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(chunkOffset), Long.BYTES); + 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 double getDouble(long address, MemoryAccessMode accessMode) { + 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 compareAndSetLong(long address, long expected, long newValue) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + return UNSAFE.compareAndSwapLong(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset, expected, newValue); + } + + @Override + public boolean compareAndSetInt(long address, int expected, int newValue) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + return UNSAFE.compareAndSwapInt(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset, expected, newValue); + } + + @Override + public long getLong(long address, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(chunkOffset), Long.BYTES); + 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 + protected byte[] allocateChunk(long bytes) { + return new byte[Math.toIntExact(bytes)]; + } + + @Override + protected long getChunkSize(long address) { + return getChunk(address).length; + } + + @Override + protected void copyBytes(long fromAddress, long toAddress, long byteSize) { + int intByteSize = Math.toIntExact(byteSize); + + byte[] fromChunk = getChunk(fromAddress); + int fromOffset = Math.toIntExact(getChunkOffset(fromAddress)); + validateAccess(fromChunk.length, fromOffset, intByteSize); + + byte[] toChunk = getChunk(toAddress); + int toOffset = Math.toIntExact(getChunkOffset(fromAddress)); + validateAccess(toChunk.length, toOffset, intByteSize); + + System.arraycopy(fromChunk, fromOffset, toChunk, toOffset, intByteSize); + } +} 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..748833dd9270 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ChunkedNativeMemory.java @@ -0,0 +1,94 @@ +package com.oracle.truffle.espresso.ffi.memory; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.List; +import java.util.Queue; + +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 final List chunks = new ArrayList<>(); + protected final Queue freeList = new ArrayDeque<>(); + + protected abstract T allocateChunk(long bytes); + + protected ChunkedNativeMemory() { + allocateMemory(0); // Sentinel block, cannot be freed. + } + + protected static int getChunkIndex(long address) { + return (int) (address >>> OFFSET_BITS); + } + + protected T getChunk(long address) { + int chunkIndex = getChunkIndex(address); + return chunks.get(chunkIndex); + } + + protected long getChunkOffset(long address) { + return address & OFFSET_MASK; + } + + protected long encodeAddress(int chunkIndex, long chunkOffset) { + if (!(Long.compareUnsigned(chunkIndex, chunks.size()) < 0)) { + throw new IllegalStateException("invalid chunk index"); + } + if (!(Long.compareUnsigned(chunkOffset, OFFSET_MASK) <= 0)) { + throw new IllegalStateException("invalid chunk offset"); + } + return (((long) chunkIndex) << OFFSET_BITS) | chunkOffset; + } + + @Override + public synchronized long allocateMemory(long bytes) { + Integer chunkIndex = freeList.poll(); + if (chunkIndex == null) { + if (chunks.size() == 1 << CHUNK_BITS) { + throw new OutOfMemoryError("cannot allocate chunk"); + } + chunks.add(null); + chunkIndex = chunks.size() - 1; + } + T chunk = allocateChunk(bytes); + chunks.set(chunkIndex, chunk); + return encodeAddress(chunkIndex, 0); + } + + @Override + public synchronized void freeMemory(long address) { + if (getChunkOffset(address) != 0) { + throw new IllegalStateException("invalid address"); + } + int chunkIndex = getChunkIndex(address); + chunks.set(chunkIndex, null); + freeList.add(chunkIndex); + } + + + public long reallocateMemory(long address, long bytes) { + if (getChunkOffset(address) != 0) { + throw new IllegalStateException("invalid address"); + } + int oldChunkIndex = getChunkIndex(address); + if (oldChunkIndex == 0) { + throw new IllegalStateException("realloc NULL"); + } + + if (getChunkSize(address) == bytes) { + return address; // no change + } + + long newAddress = allocateMemory(bytes); + copyBytes(address, newAddress, Math.min(getChunkSize(address), getChunkSize(newAddress))); + freeMemory(address); + return newAddress; + } + + protected abstract long getChunkSize(long address); + + protected abstract void copyBytes(long fromAddress, long toAddress, long byteSize); +} 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..72b4817c08ac --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/NativeMemory.java @@ -0,0 +1,84 @@ +package com.oracle.truffle.espresso.ffi.memory; + +public interface NativeMemory { + long reallocateMemory(long address, long bytes); + long allocateMemory(long bytes); + void freeMemory(long bytes); + void setMemory(long address, long bytes, byte value); + + enum MemoryAccessMode { + PLAIN, + OPAQUE, + RELEASE_ACQUIRE, + VOLATILE + } + + void putByte(long address, byte value, MemoryAccessMode accessMode); + void putShort(long address, short value, MemoryAccessMode accessMode); + void putInt(long address, int value, MemoryAccessMode accessMode); + void putLong(long address, long value, MemoryAccessMode accessMode); + + default void putBoolean(long address, boolean value, MemoryAccessMode accessMode) { + putByte(address, value ? (byte) 1 : (byte) 0, accessMode); + } + + default void putChar(long address, char value, MemoryAccessMode accessMode) { + putShort(address, (short) value, accessMode); + } + + default void putFloat(long address, float value, MemoryAccessMode accessMode) { + putInt(address, Float.floatToRawIntBits(value), accessMode); + } + + default void putDouble(long address, double value, MemoryAccessMode accessMode) { + putLong(address, Double.doubleToRawLongBits(value), accessMode); + } + + byte getByte(long address, MemoryAccessMode accessMode); + short getShort(long address, MemoryAccessMode accessMode); + int getInt(long address, MemoryAccessMode accessMode); + long getLong(long address, MemoryAccessMode accessMode); + + + default boolean getBoolean(long address, MemoryAccessMode accessMode) { + return getByte(address, accessMode) != 0; + } + + default char getChar(long address, MemoryAccessMode accessMode) { + return (char) getShort(address, accessMode); + } + + default float getFloat(long address, MemoryAccessMode accessMode) { + return Float.intBitsToFloat(getInt(address, accessMode)); + } + + default double getDouble(long address, MemoryAccessMode accessMode) { + return Double.longBitsToDouble(getLong(address, accessMode)); + } + + boolean compareAndSetLong(long address, long expected, long newValue); + boolean compareAndSetInt(long address, int expected, int newValue); + + default long compareAndExchangeLong(long address, long expected, long newValue) { + long previous; + do { + previous = getLong(address, MemoryAccessMode.VOLATILE); + if (previous != expected) { + return previous; + } + } while (!compareAndSetLong(address, expected, newValue)); + return previous; + } + + default int compareAndExchangeInt(long address, int expected, int newValue) { + int previous; + do { + previous = getInt(address, MemoryAccessMode.VOLATILE); + if (previous != expected) { + return previous; + } + } while (!compareAndSetInt(address, expected, newValue)); + return previous; + } + +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ProtoChunkedMemoryImpl.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ProtoChunkedMemoryImpl.java new file mode 100644 index 000000000000..6898effddbef --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ProtoChunkedMemoryImpl.java @@ -0,0 +1,244 @@ +package com.oracle.truffle.espresso.ffi.memory; + +import java.nio.ByteOrder; +import java.util.Arrays; + +import com.oracle.truffle.api.memory.ByteArraySupport; +import com.oracle.truffle.espresso.vm.UnsafeAccess; + +import sun.misc.Unsafe; + +public class ProtoChunkedMemoryImpl extends ChunkedNativeMemory { + + private static final ByteArraySupport BYTES = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN + ? ByteArraySupport.littleEndian() + : ByteArraySupport.bigEndian(); + + private static void validateAccess(int length, int byteIndex, int accessByteSize) { + if (byteIndex < 0 || byteIndex > length - accessByteSize) { + throw new IndexOutOfBoundsException(); + } + } + + @Override + public void setMemory(long address, long bytes, byte value) { + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(bytes), Byte.BYTES); + Arrays.fill(chunk, 0, Math.toIntExact(bytes), value); + } + + @Override + public void putBoolean(long address, boolean value, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + switch (accessMode) { + case PLAIN -> BYTES.putByte(chunk, chunkOffset, value ? (byte) 1 : (byte) 0); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> + BYTES.putByteVolatile(chunk, chunkOffset, value ? (byte) 1 : (byte) 0); + } + } + + @Override + public void putByte(long address, byte value, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(chunkOffset), Byte.BYTES); + switch (accessMode) { + case PLAIN -> BYTES.putByte(chunk, chunkOffset, value); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> BYTES.putByteVolatile(chunk, chunkOffset, value); + } + } + + @Override + public void putChar(long address, char value, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(chunkOffset), Character.BYTES); + switch (accessMode) { + case PLAIN -> BYTES.putShort(chunk, chunkOffset, (short) value); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> BYTES.putShortVolatile(chunk, chunkOffset, (short) value); + } + } + + @Override + public void putShort(long address, short value, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(chunkOffset), Short.BYTES); + switch (accessMode) { + case PLAIN -> BYTES.putShort(chunk, chunkOffset, value); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> BYTES.putShortVolatile(chunk, chunkOffset, value); + } + } + + @Override + public void putInt(long address, int value, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(chunkOffset), Integer.BYTES); + switch (accessMode) { + case PLAIN -> BYTES.putInt(chunk, chunkOffset, value); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> BYTES.putIntVolatile(chunk, chunkOffset, value); + } + } + + @Override + public void putFloat(long address, float value, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(chunkOffset), Float.BYTES); + switch (accessMode) { + case PLAIN -> BYTES.putFloat(chunk, chunkOffset, value); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> BYTES.putIntVolatile(chunk, chunkOffset, Float.floatToRawIntBits(value)); + } + } + + @Override + public void putDouble(long address, double value, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(chunkOffset), Double.BYTES); + switch (accessMode) { + case PLAIN -> BYTES.putDouble(chunk, chunkOffset, value); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> BYTES.putLongVolatile(chunk, chunkOffset, Double.doubleToRawLongBits(value)); + } + } + + @Override + public void putLong(long address, long value, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(chunkOffset), Long.BYTES); + switch (accessMode) { + case PLAIN -> BYTES.putLong(chunk, chunkOffset, value); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> BYTES.putLongVolatile(chunk, chunkOffset, value); + } + } + + @Override + public boolean getBoolean(long address, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(chunkOffset), Byte.BYTES); + return switch (accessMode) { + case PLAIN -> BYTES.getByte(chunk, chunkOffset) != 0; + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> BYTES.getByteVolatile(chunk, chunkOffset) != 0; + }; + } + + @Override + public byte getByte(long address, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(chunkOffset), Byte.BYTES); + return switch (accessMode) { + case PLAIN -> BYTES.getByte(chunk, chunkOffset); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> BYTES.getByteVolatile(chunk, chunkOffset); + }; + } + + @Override + public char getChar(long address, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(chunkOffset), Character.BYTES); + return switch (accessMode) { + case PLAIN -> (char) BYTES.getShort(chunk, chunkOffset); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> (char) BYTES.getShortVolatile(chunk, chunkOffset); + }; + } + + @Override + public short getShort(long address, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(chunkOffset), Short.BYTES); + return switch (accessMode) { + case PLAIN -> BYTES.getShort(chunk, chunkOffset); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> BYTES.getShortVolatile(chunk, chunkOffset); + }; + } + + @Override + public int getInt(long address, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(chunkOffset), Integer.BYTES); + return switch (accessMode) { + case PLAIN -> BYTES.getInt(chunk, chunkOffset); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> BYTES.getIntVolatile(chunk, chunkOffset); + }; + } + + @Override + public float getFloat(long address, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(chunkOffset), Float.BYTES); + return switch (accessMode) { + case PLAIN -> BYTES.getFloat(chunk, chunkOffset); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> Float.intBitsToFloat(BYTES.getIntVolatile(chunk, chunkOffset)); + }; + } + + @Override + public double getDouble(long address, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(chunkOffset), Double.BYTES); + return switch (accessMode) { + case PLAIN -> BYTES.getDouble(chunk, chunkOffset); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> Double.longBitsToDouble(BYTES.getLongVolatile(chunk, chunkOffset)); + }; + } + + @Override + public boolean compareAndSetLong(long address, long expected, long newValue) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + return BYTES.compareAndExchangeLong(chunk, chunkOffset, expected, newValue) == expected; + } + + @Override + public boolean compareAndSetInt(long address, int expected, int newValue) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + return BYTES.compareAndExchangeInt(chunk, chunkOffset, expected, newValue) == expected; + } + + @Override + public long getLong(long address, MemoryAccessMode accessMode) { + long chunkOffset = getChunkOffset(address); + byte[] chunk = getChunk(address); + validateAccess(chunk.length, Math.toIntExact(chunkOffset), Long.BYTES); + return switch (accessMode) { + case PLAIN -> BYTES.getLong(chunk, chunkOffset); + case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> BYTES.getLongVolatile(chunk, chunkOffset); + }; + } + + @Override + protected byte[] allocateChunk(long bytes) { + return new byte[Math.toIntExact(bytes)]; + } + + @Override + protected long getChunkSize(long address) { + return getChunk(address).length; + } + + @Override + protected void copyBytes(long fromAddress, long toAddress, long byteSize) { + int intByteSize = Math.toIntExact(byteSize); + + byte[] fromChunk = getChunk(fromAddress); + int fromOffset = Math.toIntExact(getChunkOffset(fromAddress)); + validateAccess(fromChunk.length, fromOffset, intByteSize); + + byte[] toChunk = getChunk(toAddress); + int toOffset = Math.toIntExact(getChunkOffset(fromAddress)); + validateAccess(toChunk.length, toOffset, intByteSize); + + System.arraycopy(fromChunk, fromOffset, toChunk, toOffset, intByteSize); + } +} 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..51c82d1f024b --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/UnsafeNativeMemory.java @@ -0,0 +1,167 @@ +package com.oracle.truffle.espresso.ffi.memory; + +import com.oracle.truffle.espresso.vm.UnsafeAccess; +import sun.misc.Unsafe; + +public class UnsafeNativeMemory implements NativeMemory { + + private static final Unsafe UNSAFE = UnsafeAccess.get(); + + @Override + public long reallocateMemory(long address, long bytes) { + return UNSAFE.reallocateMemory(address, bytes); + } + + @Override + public long allocateMemory(long bytes) { + return UNSAFE.allocateMemory(bytes); + } + + @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); + } +} 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..4aa5e477f289 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 @@ -40,12 +40,14 @@ import com.oracle.truffle.api.library.ExportMessage; import com.oracle.truffle.api.profiles.BranchProfile; 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.substitutions.Collect; @@ -78,7 +80,7 @@ public final class NFIIsolatedNativeAccess extends NFINativeAccess { private final DefaultLibrary defaultLibrary; NFIIsolatedNativeAccess(TruffleLanguage.Env env) { - super(env); + super(env, null); // 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); @@ -93,13 +95,56 @@ public final class NFIIsolatedNativeAccess extends NFINativeAccess { * loaded inside the isolated namespace provides a dlsym shim inside the namespace. */ this.defaultLibrary = new DefaultLibrary(this.dlsym, rtldDefault()); + + this.nativeMemory = new UnsafeNativeMemory() { + @Override + public long allocateMemory(long bytes) { + try { + @Pointer TruffleObject address = (TruffleObject) UNCACHED_INTEROP.execute(malloc, bytes); + 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) { + try { + UNCACHED_INTEROP.execute(free, RawPointer.create(address)); + } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere(e); + } + } + + @Override + public long reallocateMemory(long address, long newSize) { + try { + @Pointer + TruffleObject newAddress = (TruffleObject) UNCACHED_INTEROP.execute(realloc, address, newSize); + 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); + } + } + }; } 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(); @@ -169,56 +214,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 { 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..c1aac37d12d2 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 @@ -31,6 +31,7 @@ import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.TruffleLogger; @@ -53,12 +54,14 @@ 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.memory.NativeMemory; 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.UnsafeNativeMemory; import com.oracle.truffle.espresso.meta.EspressoError; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; import com.oracle.truffle.espresso.substitutions.Collect; @@ -82,6 +85,8 @@ public class NFINativeAccess implements NativeAccess { private final Map signatureCache; protected final TruffleLanguage.Env env; + @CompilationFinal protected NativeMemory nativeMemory; + protected static String nfiType(NativeType nativeType) { // @formatter:off switch (nativeType) { @@ -147,7 +152,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; @@ -526,4 +536,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/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..8172a55c055f 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 @@ -26,6 +26,7 @@ import static com.oracle.truffle.espresso.threads.ThreadState.PARKED; import static com.oracle.truffle.espresso.threads.ThreadState.TIMED_PARKED; +import java.lang.invoke.VarHandle; import java.lang.reflect.Array; import java.nio.ByteOrder; import java.security.ProtectionDomain; @@ -54,6 +55,8 @@ 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; @@ -453,20 +456,12 @@ 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) { + long address = meta.getNativeAccess().nativeMemory().allocateMemory(length); + if (address == 0L && 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 ptr; + return address; } /** @@ -490,18 +485,10 @@ 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) { + long newAddress = meta.getNativeAccess().nativeMemory().reallocateMemory(address, newSize); + if (newAddress == 0L) { 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 newAddress; } @@ -515,7 +502,7 @@ 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)); + meta.getNativeAccess().nativeMemory().freeMemory(address); } @Substitution(hasReceiver = true, nameProvider = SharedUnsafeAppend0.class) @@ -945,6 +932,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 +947,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) @@ -970,7 +970,7 @@ abstract static class PutByte extends UnsafeAccessNode { @Specialization void doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, byte value) { - UnsafeAccess.getIfAllowed(getMeta()).putByte(address, value); + getNativeAccess().nativeMemory().putByte(address, value, MemoryAccessMode.PLAIN); } } @@ -982,7 +982,7 @@ abstract static class PutChar extends UnsafeAccessNode { @Specialization void doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, char value) { - UnsafeAccess.getIfAllowed(getMeta()).putChar(address, value); + getNativeAccess().nativeMemory().putChar(address, value, MemoryAccessMode.PLAIN); } } @@ -994,7 +994,7 @@ abstract static class PutShort extends UnsafeAccessNode { @Specialization void doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, short value) { - UnsafeAccess.getIfAllowed(getMeta()).putShort(address, value); + getNativeAccess().nativeMemory().putShort(address, value, MemoryAccessMode.PLAIN); } } @@ -1007,7 +1007,7 @@ abstract static class PutInt extends UnsafeAccessNode { @Specialization void doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, int value) { - UnsafeAccess.getIfAllowed(getMeta()).putInt(address, value); + getNativeAccess().nativeMemory().putInt(address, value, MemoryAccessMode.PLAIN); } } @@ -1019,7 +1019,7 @@ abstract static class PutFloat extends UnsafeAccessNode { @Specialization void doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, float value) { - UnsafeAccess.getIfAllowed(getMeta()).putFloat(address, value); + getNativeAccess().nativeMemory().putFloat(address, value, MemoryAccessMode.PLAIN); } } @@ -1031,7 +1031,7 @@ abstract static class PutDouble extends UnsafeAccessNode { @Specialization void doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, double value) { - UnsafeAccess.getIfAllowed(getMeta()).putDouble(address, value); + getNativeAccess().nativeMemory().putDouble(address, value, MemoryAccessMode.PLAIN); } } @@ -1043,7 +1043,7 @@ abstract static class PutLong extends UnsafeAccessNode { @Specialization void doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address, long value) { - UnsafeAccess.getIfAllowed(getMeta()).putLong(address, value); + getNativeAccess().nativeMemory().putLong(address, value, MemoryAccessMode.PLAIN); } } @@ -1063,9 +1063,14 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, byte value) { + getNativeAccess().nativeMemory().putByte(offset, value, MemoryAccessMode.PLAIN); + } + + @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)") @@ -1143,9 +1148,14 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, boolean value) { + getNativeAccess().nativeMemory().putBoolean(offset, value, MemoryAccessMode.PLAIN); + } + + @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 +1192,13 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, char value) { + getNativeAccess().nativeMemory().putChar(offset, value, MemoryAccessMode.PLAIN); + } + + @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 +1236,13 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, short value) { + getNativeAccess().nativeMemory().putShort(offset, value, MemoryAccessMode.PLAIN); + } + + @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 +1280,13 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, int value) { + getNativeAccess().nativeMemory().putInt(offset, value, MemoryAccessMode.PLAIN); + } + + @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 +1324,13 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, float value) { + getNativeAccess().nativeMemory().putFloat(offset, value, MemoryAccessMode.PLAIN); + } + + @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 +1368,13 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, double value) { + getNativeAccess().nativeMemory().putDouble(offset, value, MemoryAccessMode.PLAIN); + } + + @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 +1412,13 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, long value) { + getNativeAccess().nativeMemory().putLong(offset, value, MemoryAccessMode.PLAIN); + } + + @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 +1462,13 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, int value) { + getNativeAccess().nativeMemory().putInt(offset, value, MemoryAccessMode.RELEASE_ACQUIRE); + } + + @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 +1506,13 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, long value) { + getNativeAccess().nativeMemory().putLong(offset, value, MemoryAccessMode.RELEASE_ACQUIRE); + } + + @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,9 +1551,15 @@ 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)") + void doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, @JavaType(Object.class) StaticObject value) { + 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 +1608,13 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return getNativeAccess().nativeMemory().getByte(offset, MemoryAccessMode.PLAIN); + } + + @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,10 +1655,17 @@ 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) - StaticObject doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, + StaticObject doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + 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()).getObject(unwrapNullOrArray(getLanguage(), holder), offset); } @@ -1638,8 +1706,13 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return getNativeAccess().nativeMemory().getBoolean(offset, MemoryAccessMode.PLAIN); + } + + @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 +1753,14 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return getNativeAccess().nativeMemory().getChar(offset, MemoryAccessMode.PLAIN); + } + + @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,11 +1800,17 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return getNativeAccess().nativeMemory().getShort(offset, MemoryAccessMode.PLAIN); + } + + @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)") static short doGeneric(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, @Bind Node node, @@ -1764,9 +1848,14 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return getNativeAccess().nativeMemory().getInt(offset, MemoryAccessMode.PLAIN); + } + + @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 +1895,14 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return getNativeAccess().nativeMemory().getFloat(offset, MemoryAccessMode.PLAIN); + } + + @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 +1942,14 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return getNativeAccess().nativeMemory().getDouble(offset, MemoryAccessMode.PLAIN); + } + + @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 +1989,14 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return getNativeAccess().nativeMemory().getLong(offset, MemoryAccessMode.PLAIN); + } + + @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 +2040,14 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return getNativeAccess().nativeMemory().getByte(offset, MemoryAccessMode.VOLATILE); + } + + @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 +2087,18 @@ 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, + StaticObject doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { - return (StaticObject) UnsafeAccess.getIfAllowed(getMeta()).getObjectVolatile(unwrapNullOrArray(getLanguage(), holder), offset); + 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 +2139,14 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return getNativeAccess().nativeMemory().getBoolean(offset, MemoryAccessMode.VOLATILE); + } + + @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 +2186,14 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return getNativeAccess().nativeMemory().getChar(offset, MemoryAccessMode.VOLATILE); + } + + @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 +2233,14 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return getNativeAccess().nativeMemory().getShort(offset, MemoryAccessMode.VOLATILE); + } + + @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,11 +2280,17 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return getNativeAccess().nativeMemory().getInt(offset, MemoryAccessMode.VOLATILE); } + @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)") static int doGeneric(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, @Bind Node node, @@ -2191,9 +2328,14 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return getNativeAccess().nativeMemory().getFloat(offset, MemoryAccessMode.VOLATILE); + } + + @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 +2375,14 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return getNativeAccess().nativeMemory().getDouble(offset, MemoryAccessMode.VOLATILE); + } + + @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 +2422,14 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset) { + return getNativeAccess().nativeMemory().getLong(offset, MemoryAccessMode.VOLATILE); + } + + @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)") @@ -2323,7 +2475,7 @@ abstract static class GetByte extends UnsafeAccessNode { @Specialization byte doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address) { - return UnsafeAccess.getIfAllowed(getMeta()).getByte(address); + return getNativeAccess().nativeMemory().getByte(address, MemoryAccessMode.PLAIN); } } @@ -2335,7 +2487,7 @@ abstract static class GetChar extends UnsafeAccessNode { @Specialization char doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address) { - return UnsafeAccess.getIfAllowed(getMeta()).getChar(address); + return getNativeAccess().nativeMemory().getChar(address, MemoryAccessMode.PLAIN); } } @@ -2347,7 +2499,7 @@ abstract static class GetShort extends UnsafeAccessNode { @Specialization short doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address) { - return UnsafeAccess.getIfAllowed(getMeta()).getShort(address); + return getNativeAccess().nativeMemory().getShort(address, MemoryAccessMode.PLAIN); } } @@ -2360,7 +2512,7 @@ abstract static class GetInt extends UnsafeAccessNode { @Specialization int doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address) { - return UnsafeAccess.getIfAllowed(getMeta()).getInt(address); + return getNativeAccess().nativeMemory().getInt(address, MemoryAccessMode.PLAIN); } } @@ -2372,7 +2524,7 @@ abstract static class GetFloat extends UnsafeAccessNode { @Specialization float doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address) { - return UnsafeAccess.getIfAllowed(getMeta()).getFloat(address); + return getNativeAccess().nativeMemory().getFloat(address, MemoryAccessMode.PLAIN); } } @@ -2384,7 +2536,7 @@ abstract static class GetDouble extends UnsafeAccessNode { @Specialization double doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address) { - return UnsafeAccess.getIfAllowed(getMeta()).getDouble(address); + return getNativeAccess().nativeMemory().getDouble(address, MemoryAccessMode.PLAIN); } } @@ -2396,7 +2548,7 @@ abstract static class GetLong extends UnsafeAccessNode { @Specialization long doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long address) { - return UnsafeAccess.getIfAllowed(getMeta()).getLong(address); + return getNativeAccess().nativeMemory().getLong(address, MemoryAccessMode.PLAIN); } } @@ -2410,9 +2562,14 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, byte value) { + getNativeAccess().nativeMemory().putByte(offset, value, MemoryAccessMode.VOLATILE); + } + + @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 +2607,16 @@ 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)") + void doNull(@SuppressWarnings("unused") @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); + 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 +2654,14 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, boolean value) { + getNativeAccess().nativeMemory().putBoolean(offset, value, MemoryAccessMode.VOLATILE); + } + + @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 +2698,14 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, char value) { + getNativeAccess().nativeMemory().putChar(offset, value, MemoryAccessMode.VOLATILE); + } + + @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 +2742,14 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, short value) { + getNativeAccess().nativeMemory().putShort(offset, value, MemoryAccessMode.VOLATILE); + } + + @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 +2786,14 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, int value) { + getNativeAccess().nativeMemory().putInt(offset, value, MemoryAccessMode.VOLATILE); + } + + @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 +2830,14 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, float value) { + getNativeAccess().nativeMemory().putFloat(offset, value, MemoryAccessMode.VOLATILE); + } + + @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 +2874,14 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, double value) { + getNativeAccess().nativeMemory().putDouble(offset, value, MemoryAccessMode.VOLATILE); + } + + @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 +2918,14 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, long value) { + getNativeAccess().nativeMemory().putLong(offset, value, MemoryAccessMode.VOLATILE); + } + + @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 +2967,16 @@ 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)") + boolean doNull(@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(unwrapNullOrArray(getLanguage(), holder), offset, before, after); + 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 +3015,16 @@ 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 doNull(@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 getNativeAccess().nativeMemory().compareAndSetInt(offset, before, after); + } + + @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(unwrapArray(getLanguage(), holder), offset, before, after); } @Specialization(guards = "!isNullOrArray(holder)") @@ -2861,10 +3071,16 @@ 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 doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, + long before, long after) { + return getNativeAccess().nativeMemory().compareAndSetLong(offset, before, after); + } + + @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 +3142,21 @@ 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) - StaticObject doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, + StaticObject doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, @JavaType(Object.class) StaticObject before, @JavaType(Object.class) StaticObject after) { + throw EspressoError.shouldNotReachHere("CAS reference on 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, + @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 +3200,16 @@ 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 doNull(@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 getNativeAccess().nativeMemory().compareAndExchangeInt(offset, before, after); + } + + @Specialization(guards = "isArray(holder)") + int doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, + int before, int after) { + return UnsafeSupport.compareAndExchangeInt(unwrapArray(getLanguage(), holder), offset, before, after); } @Specialization(guards = "!isNullOrArray(holder)") From 64b987a405c93f9fce14c64eede2870e49b0247d Mon Sep 17 00:00:00 2001 From: Elia Trachsel Date: Wed, 13 Aug 2025 15:09:54 +0200 Subject: [PATCH 11/26] Introduction of full no-native access mode This includes: - Substitutions for "startup" native libraries (libnespresso, libjvm, libjimage). Most of the methods are just nops. - We needed to add flags to disable signature mangling and enable relaxed type checks in the SubstitutionProcessor for the startup libraries. - Added isValidFor to the Lib.Factory class to maintain the mixed-no-native-access mode. More specifically for the startup libraries we only substitute in no-native mode. - Added substitutions for LibManagement - Substitutions for PlatformSocketOptions to avoid reaching LinuxSocketOptions --- .../espresso/processor/SubstitutionFlag.java | 12 ++ .../processor/SubstitutionProcessor.java | 20 +-- .../truffle/espresso/EspressoLanguage.java | 14 +- .../espresso/descriptors/EspressoSymbols.java | 13 ++ .../ffi/EspressoLibsNativeAccess.java | 2 +- .../espresso/libs/InformationLeak.java | 16 +++ .../com/oracle/truffle/espresso/libs/Lib.java | 30 ++++- .../oracle/truffle/espresso/libs/Libs.java | 7 +- .../truffle/espresso/libs/LibsMeta.java | 58 ++++++++ .../libs/SubstitutionFactoryWrapper.java | 19 +++ .../espresso/libs/libjimage/LibJimage.java | 50 +++++++ ...jdk_internal_jimage_NativeImageBuffer.java | 42 ++++++ .../truffle/espresso/libs/libjvm/LibJVM.java | 50 +++++++ .../libs/libjvm/impl/LibJVMSubstitutions.java | 124 ++++++++++++++++++ .../libs/libmanagement/LibManagement.java | 42 ++++++ .../Target_sun_management_MemoryImpl.java | 52 ++++++++ .../Target_sun_management_MemoryPoolImpl.java | 51 +++++++ .../Target_sun_management_ThreadImpl.java | 39 ++++++ ...arget_sun_management_VMManagementImpl.java | 100 ++++++++++++++ .../libmanagement_ext/LibManagementExt.java | 42 ++++++ ...nagement_internal_OperatingSystemImpl.java | 38 ++++++ .../libs/libnespresso/LibNespresso.java | 50 +++++++ .../impl/LibNespressoSubstitutions.java | 103 +++++++++++++++ .../espresso/runtime/EspressoContext.java | 4 + .../substitutions/SubstitutionFlag.java | 6 + ...edSocketOptions_PlatformSocketOptions.java | 45 +++++++ .../standard/Target_sun_misc_Unsafe.java | 1 + .../truffle/espresso/vm/Management.java | 14 ++ 28 files changed, 1023 insertions(+), 21 deletions(-) create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjimage/LibJimage.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjimage/impl/Target_jdk_internal_jimage_NativeImageBuffer.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjvm/LibJVM.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjvm/impl/LibJVMSubstitutions.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement/LibManagement.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement/impl/Target_sun_management_MemoryImpl.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement/impl/Target_sun_management_MemoryPoolImpl.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement/impl/Target_sun_management_ThreadImpl.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement/impl/Target_sun_management_VMManagementImpl.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement_ext/LibManagementExt.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement_ext/impl/Target_com_sun_management_internal_OperatingSystemImpl.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnespresso/LibNespresso.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnespresso/impl/LibNespressoSubstitutions.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_jdk_net_ExtendedSocketOptions_PlatformSocketOptions.java diff --git a/espresso/src/com.oracle.truffle.espresso.processor/src/com/oracle/truffle/espresso/processor/SubstitutionFlag.java b/espresso/src/com.oracle.truffle.espresso.processor/src/com/oracle/truffle/espresso/processor/SubstitutionFlag.java index f4fd19dee84b..60d953fc4415 100644 --- a/espresso/src/com.oracle.truffle.espresso.processor/src/com/oracle/truffle/espresso/processor/SubstitutionFlag.java +++ b/espresso/src/com.oracle.truffle.espresso.processor/src/com/oracle/truffle/espresso/processor/SubstitutionFlag.java @@ -53,6 +53,18 @@ public final class SubstitutionFlag { */ public static final byte InlineInBytecode = 0b00000010; + /** + * Whether the given library entry implementation needs to have its method signature mangled for + * registration. This can happen if a class has overloaded native methods. + */ + public static final byte needsSignatureMangle = 0b00000100; + + /** + * Whether to relax type checks in the substitution processor. Used for substitutions in + * libnespresso. + */ + public static final byte relaxTypeChecks = 0b00001000; + private SubstitutionFlag() { } } diff --git a/espresso/src/com.oracle.truffle.espresso.processor/src/com/oracle/truffle/espresso/processor/SubstitutionProcessor.java b/espresso/src/com.oracle.truffle.espresso.processor/src/com/oracle/truffle/espresso/processor/SubstitutionProcessor.java index c0bf4918f5be..b6befc7306f7 100644 --- a/espresso/src/com.oracle.truffle.espresso.processor/src/com/oracle/truffle/espresso/processor/SubstitutionProcessor.java +++ b/espresso/src/com.oracle.truffle.espresso.processor/src/com/oracle/truffle/espresso/processor/SubstitutionProcessor.java @@ -313,9 +313,16 @@ void processSubstitution(Element element, String className, TypeMirror defaultNa // Find the methods annotated with @Substitution. AnnotationMirror subst = getAnnotation(element, substitutionAnnotation); if (subst != null) { + List flagsList = getAnnotationValueList(subst, "flags", Byte.class); + byte flags = 0; + for (Byte flag : flagsList) { + flags |= flag; + } - // Sanity check. - checkSubstitutionElement(element); + if (!isFlag(flags, SubstitutionFlag.relaxTypeChecks)) { + // Sanity check. + checkSubstitutionElement(element); + } // Obtain the name of the element to be substituted in. String targetMethodName = getSubstutitutedMethodName(element); @@ -347,12 +354,6 @@ void processSubstitution(Element element, String className, TypeMirror defaultNa TypeMirror languageFilter = getLanguageFilter(subst); - List flagsList = getAnnotationValueList(subst, "flags", Byte.class); - byte flags = 0; - for (Byte flag : flagsList) { - flags |= flag; - } - TypeMirror encodedInlineGuard = getInlineValue(classWideInline, element); boolean inlineInBytecode = encodedInlineGuard != null || // Implicit inlining of trivial substitutions. @@ -626,6 +627,9 @@ List expectedImports(String className, String targetMethodName, List sun_nio_fs_TruffleFilteredDirectoryStream$ForeignDirectoryStream = SYMBOLS.putType("Lsun/nio/fs/TruffleFilteredDirectoryStream$ForeignDirectoryStream;"); public static final Symbol sun_nio_fs_TruffleFilteredDirectoryStream$ForeignIterator = SYMBOLS.putType("Lsun/nio/fs/TruffleFilteredDirectoryStream$ForeignIterator;"); + // libextnet + public static final Symbol jdk_net_ExtendedSocketOptions$PlatformSocketOptions = SYMBOLS.putType("Ljdk/net/ExtendedSocketOptions$PlatformSocketOptions;"); + // libmanagement + public static final Symbol sun_management_VMManagementImpl = SYMBOLS.putType("Lsun/management/VMManagementImpl;"); // URL class loader public static final Symbol java_net_URLClassLoader = SYMBOLS.putType("Ljava/net/URLClassLoader;"); public static final Symbol java_net_URL = SYMBOLS.putType("Ljava/net/URL;"); @@ -819,6 +823,15 @@ public static class Names { public static final Symbol totalTime = SYMBOLS.putName("totalTime"); public static final Symbol user = SYMBOLS.putName("user"); public static final Symbol initialized = SYMBOLS.putName("initialized"); + // java.management + public static final Symbol compTimeMonitoringSupport = SYMBOLS.putName("compTimeMonitoringSupport"); + public static final Symbol threadContentionMonitoringSupport = SYMBOLS.putName("threadContentionMonitoringSupport"); + public static final Symbol currentThreadCpuTimeSupport = SYMBOLS.putName("currentThreadCpuTimeSupport"); + public static final Symbol otherThreadCpuTimeSupport = SYMBOLS.putName("otherThreadCpuTimeSupport"); + public static final Symbol threadAllocatedMemorySupport = SYMBOLS.putName("threadAllocatedMemorySupport"); + public static final Symbol remoteDiagnosticCommandsSupport = SYMBOLS.putName("remoteDiagnosticCommandsSupport"); + public static final Symbol objectMonitorUsageSupport = SYMBOLS.putName("objectMonitorUsageSupport"); + public static final Symbol synchronizerUsageSupport = SYMBOLS.putName("synchronizerUsageSupport"); // java.lang.invoke.* // CallSite public static final Symbol target = SYMBOLS.putName("target"); 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 5618cc90ec4c..552ffb9649ab 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 @@ -62,7 +62,7 @@ private static TruffleLogger getLogger() { public EspressoLibsNativeAccess(EspressoContext ctx, NativeAccess delegate) { super(ctx); this.delegate = delegate; - this.libs = new Libs(); + this.libs = new Libs(ctx.getLanguage()); } @Override diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/InformationLeak.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/InformationLeak.java index dc9e4dced529..148bc3a34f9f 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/InformationLeak.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/InformationLeak.java @@ -37,7 +37,9 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.espresso.io.Throw; +import com.oracle.truffle.espresso.meta.EspressoError; import com.oracle.truffle.espresso.runtime.EspressoContext; +import com.oracle.truffle.espresso.vm.Management; /** * In the context of the EspressoLibs project, this class is designed to aggregate methods and @@ -126,4 +128,18 @@ public byte[] getMacAddress(NetworkInterface netIF) { throw Throw.throwSocketException(e, context); } } + + @TruffleBoundary + public Management checkAndGetManagement(EspressoContext context) { + if (!context.getEspressoEnv().EnableManagement) { + throw Throw.throwIllegalStateException("You are accessing LibManagement classes even though management is disabled", context); + } + Management management = context.getVM().getManagement(); + if (management == null) { + // management is only null if Management is disabled + throw EspressoError.shouldNotReachHere(); + } + return management; + } + } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/Lib.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/Lib.java index 3bf795c88322..6303d1d89be6 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/Lib.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/Lib.java @@ -28,6 +28,7 @@ import java.util.TreeMap; import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.espresso.EspressoLanguage; import com.oracle.truffle.espresso.classfile.descriptors.Name; import com.oracle.truffle.espresso.classfile.descriptors.Signature; import com.oracle.truffle.espresso.classfile.descriptors.Symbol; @@ -57,23 +58,38 @@ public interface Factory { String name(); Lib create(EspressoContext ctx); + + default boolean isValidfor(EspressoLanguage language) { + return true; + } } private final String name; public Lib(EspressoContext context, List collector, String name) { + this(context, collector, name, true); + } + + public Lib(EspressoContext context, List collector, String name, boolean mangle) { this.name = name; for (JavaSubstitution.Factory factory : collector) { if (factory.isValidFor(context.getLanguage())) { List refs = getRefs(context, factory); for (MethodKey ref : refs) { - String key = Mangle.mangleMethod( - ref.getHolderType(), - ref.getName().toString(), - factory.needsSignatureMangle() - ? ref.getSignature() - : null, - false); + String key; + + if (mangle) { + key = Mangle.mangleMethod( + ref.getHolderType(), + ref.getName().toString(), + factory.needsSignatureMangle() + ? ref.getSignature() + : null, + false); + } else { + key = ref.getName().toString(); + } + assert !bindings.containsKey(key); context.getLogger().finer(() -> "Registering " + name() + " library entry: " + key); bindings.put(key, factory); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/Libs.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/Libs.java index a42e51efb6cf..bfaf8819ef2a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/Libs.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/Libs.java @@ -24,6 +24,7 @@ import org.graalvm.collections.EconomicMap; +import com.oracle.truffle.espresso.EspressoLanguage; import com.oracle.truffle.espresso.runtime.EspressoContext; /** @@ -40,9 +41,11 @@ public final class Libs { private final EconomicMap knownLibs = EconomicMap.create(); - public Libs() { + public Libs(EspressoLanguage lang) { for (Lib.Factory lib : LibsCollector.getInstances(Lib.Factory.class)) { - knownLibs.put(lib.name(), lib); + if (lib.isValidfor(lang)) { + knownLibs.put(lib.name(), lib); + } } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsMeta.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsMeta.java index 44ecee84830e..910dd25a2eb1 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsMeta.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsMeta.java @@ -24,6 +24,7 @@ import static com.oracle.truffle.espresso.classfile.JavaVersion.VersionRange.ALL; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.espresso.classfile.descriptors.Symbol; import com.oracle.truffle.espresso.classfile.descriptors.Type; import com.oracle.truffle.espresso.descriptors.EspressoSymbols; @@ -70,6 +71,12 @@ public final class LibsMeta implements ContextAccess { public final ObjectKlass java_net_NetworkInterface; public final LibNetMeta net; + // libextnet + @CompilerDirectives.CompilationFinal public ObjectKlass jdk_net_ExtendedSocketOptions$PlatformSocketOptions; + @CompilerDirectives.CompilationFinal public Method jdk_net_ExtendedSocketOptions$PlatformSocketOptions_init; + + // libmanagement + public final LibManagementMeta management; // Checkstyle: resume field name check @Override @@ -122,6 +129,22 @@ public LibsMeta(EspressoContext ctx) { // libnet java_net_NetworkInterface = knownKlass(EspressoSymbols.Types.java_net_NetworkInterface); this.net = context.getLanguage().enableNetworking() ? new LibNetMeta() : null; + + // libmanagement + this.management = context.getEspressoEnv().EnableManagement ? new LibManagementMeta() : null; + } + + /** + * same idea as {@link Meta#postSystemInit()}. + */ + public void postSystemInit() { + // libextnet + jdk_net_ExtendedSocketOptions$PlatformSocketOptions = knownKlass(EspressoSymbols.Types.jdk_net_ExtendedSocketOptions$PlatformSocketOptions); + jdk_net_ExtendedSocketOptions$PlatformSocketOptions_init = jdk_net_ExtendedSocketOptions$PlatformSocketOptions.lookupDeclaredMethod(EspressoSymbols.Names._init_, + EspressoSymbols.Signatures._void); + if (management != null) { + management.postSystemInit(); + } } public ObjectKlass knownKlass(Symbol type) { @@ -203,4 +226,39 @@ private LibNetMeta() { } } + + public final class LibManagementMeta { + // Checkstyle: stop field name check + @CompilerDirectives.CompilationFinal public ObjectKlass sun_management_VMManagementImpl; + @CompilerDirectives.CompilationFinal public Field sun_management_VMManagementImpl_compTimeMonitoringSupport; + @CompilerDirectives.CompilationFinal public Field sun_management_VMManagementImpl_threadContentionMonitoringSupport; + @CompilerDirectives.CompilationFinal public Field sun_management_VMManagementImpl_currentThreadCpuTimeSupport; + @CompilerDirectives.CompilationFinal public Field sun_management_VMManagementImpl_otherThreadCpuTimeSupport; + @CompilerDirectives.CompilationFinal public Field sun_management_VMManagementImpl_threadAllocatedMemorySupport; + @CompilerDirectives.CompilationFinal public Field sun_management_VMManagementImpl_remoteDiagnosticCommandsSupport; + @CompilerDirectives.CompilationFinal public Field sun_management_VMManagementImpl_objectMonitorUsageSupport; + @CompilerDirectives.CompilationFinal public Field sun_management_VMManagementImpl_synchronizerUsageSupport; + // Checkstyle: resume field name check + + public void postSystemInit() { + sun_management_VMManagementImpl = knownKlass(EspressoSymbols.Types.sun_management_VMManagementImpl); + sun_management_VMManagementImpl_compTimeMonitoringSupport = sun_management_VMManagementImpl.requireDeclaredField(EspressoSymbols.Names.compTimeMonitoringSupport, + EspressoSymbols.Types._boolean); + sun_management_VMManagementImpl_threadContentionMonitoringSupport = sun_management_VMManagementImpl.requireDeclaredField(EspressoSymbols.Names.threadContentionMonitoringSupport, + EspressoSymbols.Types._boolean); + sun_management_VMManagementImpl_currentThreadCpuTimeSupport = sun_management_VMManagementImpl.requireDeclaredField(EspressoSymbols.Names.currentThreadCpuTimeSupport, + EspressoSymbols.Types._boolean); + sun_management_VMManagementImpl_otherThreadCpuTimeSupport = sun_management_VMManagementImpl.requireDeclaredField(EspressoSymbols.Names.otherThreadCpuTimeSupport, + EspressoSymbols.Types._boolean); + sun_management_VMManagementImpl_threadAllocatedMemorySupport = sun_management_VMManagementImpl.requireDeclaredField(EspressoSymbols.Names.threadAllocatedMemorySupport, + EspressoSymbols.Types._boolean); + sun_management_VMManagementImpl_remoteDiagnosticCommandsSupport = sun_management_VMManagementImpl.requireDeclaredField(EspressoSymbols.Names.remoteDiagnosticCommandsSupport, + EspressoSymbols.Types._boolean); + sun_management_VMManagementImpl_objectMonitorUsageSupport = sun_management_VMManagementImpl.requireDeclaredField(EspressoSymbols.Names.objectMonitorUsageSupport, + EspressoSymbols.Types._boolean); + sun_management_VMManagementImpl_synchronizerUsageSupport = sun_management_VMManagementImpl.requireDeclaredField(EspressoSymbols.Names.synchronizerUsageSupport, + EspressoSymbols.Types._boolean); + + } + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/SubstitutionFactoryWrapper.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/SubstitutionFactoryWrapper.java index bf4d93e28581..e2013ca3ea51 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/SubstitutionFactoryWrapper.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/SubstitutionFactoryWrapper.java @@ -22,7 +22,13 @@ */ package com.oracle.truffle.espresso.libs; +import com.oracle.truffle.api.CompilerDirectives; +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.UnsupportedTypeException; +import com.oracle.truffle.api.library.ExportLibrary; +import com.oracle.truffle.api.library.ExportMessage; import com.oracle.truffle.espresso.substitutions.JavaSubstitution; /** @@ -31,6 +37,7 @@ * substitutions to be used as the result of a call to * {@link com.oracle.truffle.espresso.ffi.NativeAccess#lookupSymbol(TruffleObject, String)}. */ +@ExportLibrary(InteropLibrary.class) public final class SubstitutionFactoryWrapper implements TruffleObject { private final JavaSubstitution.Factory subst; @@ -41,4 +48,16 @@ public final class SubstitutionFactoryWrapper implements TruffleObject { public JavaSubstitution.Factory getSubstitution() { return subst; } + + @ExportMessage + @SuppressWarnings("static-method") + public boolean isExecutable() { + return true; + } + + @ExportMessage + @CompilerDirectives.TruffleBoundary + public Object execute(Object[] args) throws ArityException, UnsupportedTypeException { + return getSubstitution().create().invoke(args); + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjimage/LibJimage.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjimage/LibJimage.java new file mode 100644 index 000000000000..f1d3cb903184 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjimage/LibJimage.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libjimage; + +import com.oracle.truffle.espresso.EspressoLanguage; +import com.oracle.truffle.espresso.ffi.NoNativeAccess; +import com.oracle.truffle.espresso.libs.Lib; +import com.oracle.truffle.espresso.libs.Libs; +import com.oracle.truffle.espresso.runtime.EspressoContext; +import com.oracle.truffle.espresso.substitutions.Collect; +import com.oracle.truffle.espresso.substitutions.JavaSubstitution; + +@Collect(Libs.class) +public final class LibJimage implements Lib.Factory { + @Override + public String name() { + return "jimage"; + } + + @Override + public Lib create(EspressoContext ctx) { + return new Lib(ctx, LibJimageCollector.getInstances(JavaSubstitution.Factory.class), + name()); + } + + @Override + public boolean isValidfor(EspressoLanguage language) { + return language.useEspressoLibs() && language.nativeBackendId().equals(NoNativeAccess.Provider.ID); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjimage/impl/Target_jdk_internal_jimage_NativeImageBuffer.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjimage/impl/Target_jdk_internal_jimage_NativeImageBuffer.java new file mode 100644 index 000000000000..fdcfaf897c3d --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjimage/impl/Target_jdk_internal_jimage_NativeImageBuffer.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libjimage.impl; + +import java.nio.ByteBuffer; + +import com.oracle.truffle.espresso.libs.libjimage.LibJimage; +import com.oracle.truffle.espresso.meta.Meta; +import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Inject; +import com.oracle.truffle.espresso.substitutions.JavaType; +import com.oracle.truffle.espresso.substitutions.Substitution; + +@EspressoSubstitutions(type = "Ljdk/internal/jimage/NativeImageBuffer;", group = LibJimage.class) +public final class Target_jdk_internal_jimage_NativeImageBuffer { + @Substitution + @SuppressWarnings("unused") + public static @JavaType(ByteBuffer.class) StaticObject getNativeMap(@JavaType(String.class) StaticObject imagePath, @Inject Meta meta) { + return StaticObject.NULL; + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjvm/LibJVM.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjvm/LibJVM.java new file mode 100644 index 000000000000..f57d6b69e863 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjvm/LibJVM.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libjvm; + +import com.oracle.truffle.espresso.EspressoLanguage; +import com.oracle.truffle.espresso.ffi.NoNativeAccess; +import com.oracle.truffle.espresso.libs.Lib; +import com.oracle.truffle.espresso.libs.Libs; +import com.oracle.truffle.espresso.runtime.EspressoContext; +import com.oracle.truffle.espresso.substitutions.Collect; +import com.oracle.truffle.espresso.substitutions.JavaSubstitution; + +@Collect(Libs.class) +public final class LibJVM implements Lib.Factory { + @Override + public String name() { + return "jvm"; + } + + @Override + public Lib create(EspressoContext ctx) { + return new Lib(ctx, LibJVMCollector.getInstances(JavaSubstitution.Factory.class), + name(), false); + } + + @Override + public boolean isValidfor(EspressoLanguage language) { + return language.useEspressoLibs() && language.nativeBackendId().equals(NoNativeAccess.Provider.ID); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjvm/impl/LibJVMSubstitutions.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjvm/impl/LibJVMSubstitutions.java new file mode 100644 index 000000000000..a4c1d15a5877 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjvm/impl/LibJVMSubstitutions.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libjvm.impl; + +import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.espresso.ffi.Pointer; +import com.oracle.truffle.espresso.ffi.RawPointer; +import com.oracle.truffle.espresso.libs.libjvm.LibJVM; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Substitution; +import com.oracle.truffle.espresso.substitutions.SubstitutionFlag; + +@EspressoSubstitutions(type = "", group = LibJVM.class) +public final class LibJVMSubstitutions { + private static final @Pointer TruffleObject sentinelPointer = new RawPointer(-1); + + @Substitution(flags = SubstitutionFlag.relaxTypeChecks) + @SuppressWarnings("unused") + public static @Pointer TruffleObject initializeMokapotContext(@Pointer TruffleObject unused1, @Pointer TruffleObject unused2) { + // nop + return null; + } + + @Substitution(flags = SubstitutionFlag.relaxTypeChecks) + @SuppressWarnings("unused") + public static void disposeMokapotContext(@Pointer TruffleObject unused1, @Pointer TruffleObject unused2) { + // nop + } + + @Substitution(flags = SubstitutionFlag.relaxTypeChecks) + @SuppressWarnings("unused") + public static @Pointer TruffleObject initializeManagementContext(@Pointer TruffleObject unused1, int unused2) { + return null; + } + + @Substitution(flags = SubstitutionFlag.relaxTypeChecks) + @SuppressWarnings("unused") + public static void disposeManagementContext(@Pointer TruffleObject unused1, int unused2, @Pointer TruffleObject unused3) { + // nop + } + + @Substitution(flags = SubstitutionFlag.relaxTypeChecks) + @SuppressWarnings("unused") + public static void initializeStructs(@Pointer TruffleObject unused) { + // nop + } + + @Substitution(flags = SubstitutionFlag.relaxTypeChecks) + @SuppressWarnings("unused") + public static long lookupMemberOffset(@Pointer TruffleObject unused1, @Pointer TruffleObject unused2) { + return -1; + } + + @Substitution(flags = SubstitutionFlag.relaxTypeChecks) + @SuppressWarnings("unused") + public static @Pointer TruffleObject initializeJvmtiContext(@Pointer TruffleObject unused1, int unused2) { + return null; + } + + @Substitution(flags = SubstitutionFlag.relaxTypeChecks) + @SuppressWarnings("unused") + public static void disposeJvmtiContext(@Pointer TruffleObject unused1, int unused2, @Pointer TruffleObject unused3) { + // nop + } + + @Substitution(flags = SubstitutionFlag.relaxTypeChecks) + @SuppressWarnings("unused") + public static @Pointer TruffleObject getJavaVM(@Pointer TruffleObject unused) { + return null; + } + + @Substitution(flags = SubstitutionFlag.relaxTypeChecks) + @SuppressWarnings("unused") + public static void mokapotAttachThread(@Pointer TruffleObject unused) { + // nop + } + + @Substitution(flags = SubstitutionFlag.relaxTypeChecks) + @SuppressWarnings("unused") + public static void mokapotCaptureState(@Pointer TruffleObject unused1, int unused2) { + // nop + } + + @Substitution(flags = SubstitutionFlag.relaxTypeChecks, methodName = "mokapotGetRTLD_DEFAULT") + @SuppressWarnings("unused") + public static @Pointer TruffleObject mokapotGetRTLDDEFAULT() { + // nop + return sentinelPointer; + } + + @Substitution(flags = SubstitutionFlag.relaxTypeChecks) + @SuppressWarnings("unused") + public static @Pointer TruffleObject mokapotGetProcessHandle() { + // nop + return sentinelPointer; + } + + @Substitution(flags = SubstitutionFlag.relaxTypeChecks) + @SuppressWarnings("unused") + public static @Pointer TruffleObject getPackageAt(@Pointer TruffleObject unused1, int unused2) { + // nop + return null; + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement/LibManagement.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement/LibManagement.java new file mode 100644 index 000000000000..b29f36241bed --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement/LibManagement.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libmanagement; + +import com.oracle.truffle.espresso.libs.Lib; +import com.oracle.truffle.espresso.libs.Libs; +import com.oracle.truffle.espresso.runtime.EspressoContext; +import com.oracle.truffle.espresso.substitutions.Collect; +import com.oracle.truffle.espresso.substitutions.JavaSubstitution; + +@Collect(Libs.class) +public final class LibManagement implements Lib.Factory { + @Override + public String name() { + return "management"; + } + + @Override + public Lib create(EspressoContext ctx) { + return new Lib(ctx, LibManagementCollector.getInstances(JavaSubstitution.Factory.class), name()); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement/impl/Target_sun_management_MemoryImpl.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement/impl/Target_sun_management_MemoryImpl.java new file mode 100644 index 000000000000..33ca98794dce --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement/impl/Target_sun_management_MemoryImpl.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libmanagement.impl; + +import java.lang.management.MemoryManagerMXBean; +import java.lang.management.MemoryPoolMXBean; + +import com.oracle.truffle.espresso.libs.InformationLeak; +import com.oracle.truffle.espresso.libs.libmanagement.LibManagement; +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; +import com.oracle.truffle.espresso.substitutions.JavaType; +import com.oracle.truffle.espresso.substitutions.Substitution; +import com.oracle.truffle.espresso.vm.Management; + +@EspressoSubstitutions(type = "Lsun/management/MemoryImpl;", group = LibManagement.class) +public final class Target_sun_management_MemoryImpl { + + @Substitution + public static @JavaType(MemoryManagerMXBean[].class) StaticObject getMemoryManagers0(@Inject InformationLeak iL, @Inject EspressoContext context) { + Management management = iL.checkAndGetManagement(context); + return management.GetMemoryManagers(StaticObject.NULL, context.getMeta()); + } + + @Substitution + public static @JavaType(MemoryPoolMXBean[].class) StaticObject getMemoryPools0(@Inject InformationLeak iL, @Inject EspressoContext context) { + Management management = iL.checkAndGetManagement(context); + return management.GetMemoryPools(StaticObject.NULL, context.getMeta()); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement/impl/Target_sun_management_MemoryPoolImpl.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement/impl/Target_sun_management_MemoryPoolImpl.java new file mode 100644 index 000000000000..20d65586123c --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement/impl/Target_sun_management_MemoryPoolImpl.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libmanagement.impl; + +import java.lang.management.MemoryUsage; + +import com.oracle.truffle.espresso.libs.InformationLeak; +import com.oracle.truffle.espresso.libs.libmanagement.LibManagement; +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; +import com.oracle.truffle.espresso.substitutions.JavaType; +import com.oracle.truffle.espresso.substitutions.Substitution; +import com.oracle.truffle.espresso.vm.Management; + +@EspressoSubstitutions(type = "Lsun/management/MemoryPoolImpl;", group = LibManagement.class) +public final class Target_sun_management_MemoryPoolImpl { + @Substitution(hasReceiver = true) + public static @JavaType(MemoryUsage.class) StaticObject getUsage0(@JavaType(internalName = "Lsun/management/MemoryPoolImpl;") StaticObject self, @Inject InformationLeak iL, + @Inject EspressoContext context, @Inject Meta meta) { + Management management = iL.checkAndGetManagement(context); + @JavaType(MemoryUsage.class) + StaticObject memUsage = management.GetMemoryPoolUsage(self, meta); + if (StaticObject.isNull(memUsage)) { + throw meta.throwExceptionWithMessage(meta.java_lang_InternalError, "Memory Pool not found"); + } + return memUsage; + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement/impl/Target_sun_management_ThreadImpl.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement/impl/Target_sun_management_ThreadImpl.java new file mode 100644 index 000000000000..1eda0d89b08b --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement/impl/Target_sun_management_ThreadImpl.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libmanagement.impl; + +import com.oracle.truffle.espresso.libs.libmanagement.LibManagement; +import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Inject; +import com.oracle.truffle.espresso.substitutions.JavaType; +import com.oracle.truffle.espresso.substitutions.Substitution; +import com.oracle.truffle.espresso.vm.VM; + +@EspressoSubstitutions(type = "Lsun/management/ThreadImpl;", group = LibManagement.class) +public final class Target_sun_management_ThreadImpl { + @Substitution + public static @JavaType(Thread[].class) StaticObject getThreads(@Inject VM vm) { + return vm.JVM_GetAllThreads(StaticObject.NULL); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement/impl/Target_sun_management_VMManagementImpl.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement/impl/Target_sun_management_VMManagementImpl.java new file mode 100644 index 000000000000..0f26f6275790 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement/impl/Target_sun_management_VMManagementImpl.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libmanagement.impl; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.espresso.EspressoLanguage; +import com.oracle.truffle.espresso.libs.InformationLeak; +import com.oracle.truffle.espresso.libs.LibsMeta; +import com.oracle.truffle.espresso.libs.libmanagement.LibManagement; +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; +import com.oracle.truffle.espresso.substitutions.JavaType; +import com.oracle.truffle.espresso.substitutions.Substitution; +import com.oracle.truffle.espresso.vm.Management; +import com.oracle.truffle.espresso.vm.VM; + +@EspressoSubstitutions(type = "Lsun/management/VMManagementImpl;", group = LibManagement.class) +public final class Target_sun_management_VMManagementImpl { + + @CompilerDirectives.TruffleBoundary + @Substitution + public static @JavaType(String.class) StaticObject getVersion0(@Inject InformationLeak iL, @Inject Meta meta, @Inject EspressoContext context) { + Management management = iL.checkAndGetManagement(context); + int jmmVersion = management.GetVersion(); + int major = (jmmVersion & 0x0FFF0000) >>> 16; + int minor = (jmmVersion & 0x0000FF00) >>> 8; + return meta.toGuestString(major + "." + minor); + } + + @Substitution + @CompilerDirectives.TruffleBoundary + public static void initOptionalSupportFields(@Inject InformationLeak iL, @Inject LibsMeta libsMeta, @Inject EspressoContext context) { + Management management = iL.checkAndGetManagement(context); + management.initOptionalSupportFields(libsMeta); + } + + @Substitution + public static boolean isThreadCpuTimeEnabled(@Inject InformationLeak iL, @Inject EspressoContext context) { + Management management = iL.checkAndGetManagement(context); + return management.GetBoolAttribute(Management.JMM_THREAD_CPU_TIME); + } + + @Substitution + public static boolean isThreadAllocatedMemoryEnabled(@Inject InformationLeak iL, @Inject EspressoContext context) { + Management management = iL.checkAndGetManagement(context); + return management.GetBoolAttribute(Management.JMM_THREAD_ALLOCATED_MEMORY); + } + + @Substitution(hasReceiver = true) + @SuppressWarnings("unused") + public static long getStartupTime(@JavaType(internalName = "Lsun/management/VMManagementImpl;") StaticObject self, @Inject InformationLeak iL, @Inject EspressoContext context) { + Management management = iL.checkAndGetManagement(context); + return management.GetLongAttribute(StaticObject.NULL, Management.JMM_JVM_INIT_DONE_TIME_MS); + } + + @Substitution(hasReceiver = true) + @SuppressWarnings("unused") + public static long getUptime0(@JavaType(internalName = "Lsun/management/VMManagementImpl;") StaticObject self, @Inject InformationLeak iL, @Inject EspressoContext context) { + Management management = iL.checkAndGetManagement(context); + return management.GetLongAttribute(StaticObject.NULL, Management.JMM_JVM_UPTIME_MS); + } + + @Substitution(hasReceiver = true) + @SuppressWarnings("unused") + public static int getProcessId(@JavaType(internalName = "Lsun/management/VMManagementImpl;") StaticObject self, @Inject InformationLeak iL, @Inject EspressoContext context) { + Management management = iL.checkAndGetManagement(context); + // In the native code the cast without a check. We could maybe do the same then. + return Math.toIntExact(management.GetLongAttribute(StaticObject.NULL, Management.JMM_OS_PROCESS_ID)); + } + + @Substitution(hasReceiver = true) + @SuppressWarnings("unused") + public static @JavaType(String[].class) StaticObject getVmArguments0(@JavaType(internalName = "Lsun/management/VMManagementImpl;") StaticObject unusedSelf, @Inject EspressoLanguage lang, + @Inject VM vm) { + return vm.JVM_GetVmArguments(lang); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement_ext/LibManagementExt.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement_ext/LibManagementExt.java new file mode 100644 index 000000000000..a0225790c59d --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement_ext/LibManagementExt.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libmanagement_ext; + +import com.oracle.truffle.espresso.libs.Lib; +import com.oracle.truffle.espresso.libs.Libs; +import com.oracle.truffle.espresso.runtime.EspressoContext; +import com.oracle.truffle.espresso.substitutions.Collect; +import com.oracle.truffle.espresso.substitutions.JavaSubstitution; + +@Collect(Libs.class) +public final class LibManagementExt implements Lib.Factory { + @Override + public String name() { + return "management_ext"; + } + + @Override + public Lib create(EspressoContext ctx) { + return new Lib(ctx, LibManagementExtCollector.getInstances(JavaSubstitution.Factory.class), name()); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement_ext/impl/Target_com_sun_management_internal_OperatingSystemImpl.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement_ext/impl/Target_com_sun_management_internal_OperatingSystemImpl.java new file mode 100644 index 000000000000..76a489214ebb --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libmanagement_ext/impl/Target_com_sun_management_internal_OperatingSystemImpl.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libmanagement_ext.impl; + +import com.oracle.truffle.espresso.libs.InformationLeak; +import com.oracle.truffle.espresso.libs.libmanagement_ext.LibManagementExt; +import com.oracle.truffle.espresso.runtime.EspressoContext; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Inject; +import com.oracle.truffle.espresso.substitutions.Substitution; + +@EspressoSubstitutions(type = "Lcom/sun/management/internal/OperatingSystemImpl;", group = LibManagementExt.class) +public final class Target_com_sun_management_internal_OperatingSystemImpl { + @Substitution + public static void initialize0(@Inject InformationLeak iL, @Inject EspressoContext context) { + iL.checkAndGetManagement(context); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnespresso/LibNespresso.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnespresso/LibNespresso.java new file mode 100644 index 000000000000..70349cf634e7 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnespresso/LibNespresso.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libnespresso; + +import com.oracle.truffle.espresso.EspressoLanguage; +import com.oracle.truffle.espresso.ffi.NoNativeAccess; +import com.oracle.truffle.espresso.libs.Lib; +import com.oracle.truffle.espresso.libs.Libs; +import com.oracle.truffle.espresso.runtime.EspressoContext; +import com.oracle.truffle.espresso.substitutions.Collect; +import com.oracle.truffle.espresso.substitutions.JavaSubstitution; + +@Collect(Libs.class) +public final class LibNespresso implements Lib.Factory { + @Override + public String name() { + return "nespresso"; + } + + @Override + public Lib create(EspressoContext ctx) { + return new Lib(ctx, LibNespressoCollector.getInstances(JavaSubstitution.Factory.class), + name(), false); + } + + @Override + public boolean isValidfor(EspressoLanguage language) { + return language.useEspressoLibs() && language.nativeBackendId().equals(NoNativeAccess.Provider.ID); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnespresso/impl/LibNespressoSubstitutions.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnespresso/impl/LibNespressoSubstitutions.java new file mode 100644 index 000000000000..d798d15054c3 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libnespresso/impl/LibNespressoSubstitutions.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libnespresso.impl; + +import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.espresso.ffi.Pointer; +import com.oracle.truffle.espresso.libs.libnespresso.LibNespresso; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.JavaSubstitution; +import com.oracle.truffle.espresso.substitutions.Substitution; +import com.oracle.truffle.espresso.substitutions.SubstitutionFlag; + +@EspressoSubstitutions(type = "", group = LibNespresso.class) +public final class LibNespressoSubstitutions { + @Substitution(flags = SubstitutionFlag.relaxTypeChecks) + @SuppressWarnings("unused") + public static @Pointer TruffleObject initializeNativeContext(@Pointer TruffleObject unused) { + // nop + return null; + } + + @Substitution(flags = SubstitutionFlag.relaxTypeChecks) + @SuppressWarnings("unused") + public static void disposeNativeContext(@Pointer TruffleObject unused1, @Pointer TruffleObject unused2) { + // nop + } + + // Checkstyle: stop field name check + @Substitution(flags = SubstitutionFlag.relaxTypeChecks) + @SuppressWarnings("unused") + public static boolean pop_boolean(@Pointer TruffleObject unused) { + throw JavaSubstitution.shouldNotReachHere(); + } + + @Substitution(flags = SubstitutionFlag.relaxTypeChecks) + @SuppressWarnings("unused") + public static byte pop_byte(@Pointer TruffleObject unused) { + throw JavaSubstitution.shouldNotReachHere(); + } + + @Substitution(flags = SubstitutionFlag.relaxTypeChecks) + @SuppressWarnings("unused") + public static char pop_char(@Pointer TruffleObject unused) { + throw JavaSubstitution.shouldNotReachHere(); + } + + @Substitution(flags = SubstitutionFlag.relaxTypeChecks) + @SuppressWarnings("unused") + public static short pop_short(@Pointer TruffleObject unused) { + throw JavaSubstitution.shouldNotReachHere(); + } + + @Substitution(flags = SubstitutionFlag.relaxTypeChecks) + @SuppressWarnings("unused") + public static int pop_int(@Pointer TruffleObject unused) { + throw JavaSubstitution.shouldNotReachHere(); + } + + @Substitution(flags = SubstitutionFlag.relaxTypeChecks) + @SuppressWarnings("unused") + public static float pop_float(@Pointer TruffleObject unused) { + throw JavaSubstitution.shouldNotReachHere(); + } + + @Substitution(flags = SubstitutionFlag.relaxTypeChecks) + @SuppressWarnings("unused") + public static double pop_double(@Pointer TruffleObject unused) { + throw JavaSubstitution.shouldNotReachHere(); + } + + @Substitution(flags = SubstitutionFlag.relaxTypeChecks) + @SuppressWarnings("unused") + public static long pop_long(@Pointer TruffleObject unused) { + throw JavaSubstitution.shouldNotReachHere(); + } + + @Substitution(flags = SubstitutionFlag.relaxTypeChecks) + @SuppressWarnings("unused") + public static Object pop_object(@Pointer TruffleObject unused) { + throw JavaSubstitution.shouldNotReachHere(); + } + // Checkstyle: resume field name check +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java index 3663411567b6..b2e6b07b9ec9 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java @@ -597,6 +597,10 @@ private void spawnVM() throws ContextPatchingException { meta.postSystemInit(); if (language.useEspressoLibs()) { truffleIO.postSystemInit(); + libsMeta.postSystemInit(); + } + if (getLanguage().useEspressoLibs()) { + getLibsMeta().postSystemInit(); } // class redefinition will be enabled if debug mode or if any redefine or retransform diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/SubstitutionFlag.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/SubstitutionFlag.java index ceba0aea9ea7..ef33a4ccec0a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/SubstitutionFlag.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/SubstitutionFlag.java @@ -59,6 +59,12 @@ public final class SubstitutionFlag { */ public static final byte needsSignatureMangle = 0b00000100; + /** + * Whether to relax type checks in the substitution processor. Used for substitutions in + * libnespresso. + */ + public static final byte relaxTypeChecks = 0b00001000; + private SubstitutionFlag() { } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_jdk_net_ExtendedSocketOptions_PlatformSocketOptions.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_jdk_net_ExtendedSocketOptions_PlatformSocketOptions.java new file mode 100644 index 000000000000..7ff126ce2cbb --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_jdk_net_ExtendedSocketOptions_PlatformSocketOptions.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021, 2021, 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.substitutions.standard; + +import com.oracle.truffle.espresso.libs.LibsMeta; +import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Inject; +import com.oracle.truffle.espresso.substitutions.JavaType; +import com.oracle.truffle.espresso.substitutions.Substitution; +import com.oracle.truffle.espresso.substitutions.libs.EspressoLibsFilter; + +@EspressoSubstitutions(type = "Ljdk/net/ExtendedSocketOptions$PlatformSocketOptions;") +public final class Target_jdk_net_ExtendedSocketOptions_PlatformSocketOptions { + + @Substitution(languageFilter = EspressoLibsFilter.class) + public static @JavaType(internalName = "Ljdk/net/ExtendedSocketOptions$PlatformSocketOptions;") StaticObject create( + @Inject LibsMeta libsMeta) { + @JavaType(internalName = "Ljdk/net/ExtendedSocketOptions$PlatformSocketOptions;") + StaticObject nonPlatformSpecificOptions = libsMeta.jdk_net_ExtendedSocketOptions$PlatformSocketOptions.allocateInstance(libsMeta.getContext()); + libsMeta.jdk_net_ExtendedSocketOptions$PlatformSocketOptions_init.invokeDirectSpecial( + /* this */ nonPlatformSpecificOptions); + return nonPlatformSpecificOptions; + } +} 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..b0524d7fd20a 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 @@ -450,6 +450,7 @@ public static void registerNatives() { @TruffleBoundary @Substitution(hasReceiver = true, nameProvider = SharedUnsafeAppend0.class) public static long allocateMemory(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, long length, @Inject Meta meta) { + if (length < 0) { throw meta.throwExceptionWithMessage(meta.java_lang_IllegalArgumentException, "requested size is negative"); } 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 f9c9d55070cc..955f321a461d 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 @@ -63,6 +63,7 @@ import com.oracle.truffle.espresso.impl.Klass; import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.jni.NativeEnv; +import com.oracle.truffle.espresso.libs.LibsMeta; import com.oracle.truffle.espresso.meta.EspressoError; import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.runtime.EspressoContext; @@ -198,6 +199,19 @@ private ThreadMXBean getHostThreadMXBean() { return threadMXBean; } + public void initOptionalSupportFields(LibsMeta libsMeta) { + ThreadMXBean hostBean = getHostThreadMXBean(); + StaticObject staticObject = libsMeta.management.sun_management_VMManagementImpl.tryInitializeAndGetStatics(); + libsMeta.management.sun_management_VMManagementImpl_compTimeMonitoringSupport.setBoolean(staticObject, false); + libsMeta.management.sun_management_VMManagementImpl_threadContentionMonitoringSupport.setBoolean(staticObject, false); + libsMeta.management.sun_management_VMManagementImpl_currentThreadCpuTimeSupport.setBoolean(staticObject, hostBean.isCurrentThreadCpuTimeSupported()); + libsMeta.management.sun_management_VMManagementImpl_otherThreadCpuTimeSupport.setBoolean(staticObject, hostBean.isThreadCpuTimeSupported()); + libsMeta.management.sun_management_VMManagementImpl_threadAllocatedMemorySupport.setBoolean(staticObject, false); + libsMeta.management.sun_management_VMManagementImpl_remoteDiagnosticCommandsSupport.setBoolean(staticObject, false); + libsMeta.management.sun_management_VMManagementImpl_objectMonitorUsageSupport.setBoolean(staticObject, false); + libsMeta.management.sun_management_VMManagementImpl_synchronizerUsageSupport.setBoolean(staticObject, false); + } + /** * Procedure to support a new management version in Espresso: *
    From 21c8990cd6ffa83b9b7a090aded7280b0d33c209 Mon Sep 17 00:00:00 2001 From: Elia Trachsel Date: Mon, 14 Jul 2025 11:28:30 +0200 Subject: [PATCH 12/26] beginning of AWT substitutions --- .../truffle/espresso/libs/libawt/LibAwt.java | 44 +++++++++++++++++++ .../impl/Target_java_awt_Rectangle.java | 26 +++++++++++ .../libawt/impl/Target_java_awt_Toolkit.java | 35 +++++++++++++++ .../Target_java_awt_image_ColorModel.java | 35 +++++++++++++++ .../impl/Target_java_awt_image_Raster.java | 35 +++++++++++++++ .../Target_java_awt_image_SampleModel.java | 35 +++++++++++++++ 6 files changed, 210 insertions(+) create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libawt/LibAwt.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libawt/impl/Target_java_awt_Rectangle.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libawt/impl/Target_java_awt_Toolkit.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libawt/impl/Target_java_awt_image_ColorModel.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libawt/impl/Target_java_awt_image_Raster.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libawt/impl/Target_java_awt_image_SampleModel.java diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libawt/LibAwt.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libawt/LibAwt.java new file mode 100644 index 000000000000..47e4c5ef74ff --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libawt/LibAwt.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libawt; + +import com.oracle.truffle.espresso.libs.Lib; +import com.oracle.truffle.espresso.libs.Libs; +import com.oracle.truffle.espresso.runtime.EspressoContext; +import com.oracle.truffle.espresso.substitutions.Collect; +import com.oracle.truffle.espresso.substitutions.JavaSubstitution; + +@Collect(Libs.class) +public final class LibAwt implements Lib.Factory { + @Override + public String name() { + return "awt"; + } + + @Override + public Lib create(EspressoContext ctx) { + // return new Lib(ctx, List.of(), name()); + return new Lib(ctx, LibAwtCollector.getInstances(JavaSubstitution.Factory.class), + name()); + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libawt/impl/Target_java_awt_Rectangle.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libawt/impl/Target_java_awt_Rectangle.java new file mode 100644 index 000000000000..20face14978c --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libawt/impl/Target_java_awt_Rectangle.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libawt.impl; + +public class Target_java_awt_Rectangle { +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libawt/impl/Target_java_awt_Toolkit.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libawt/impl/Target_java_awt_Toolkit.java new file mode 100644 index 000000000000..f7b8571ff26b --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libawt/impl/Target_java_awt_Toolkit.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libawt.impl; + +import com.oracle.truffle.espresso.libs.libawt.LibAwt; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Substitution; + +@EspressoSubstitutions(type = "Ljava/awt/Toolkit;", group = LibAwt.class) +public final class Target_java_awt_Toolkit { + @Substitution + public static void initIDs() { + // nop + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libawt/impl/Target_java_awt_image_ColorModel.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libawt/impl/Target_java_awt_image_ColorModel.java new file mode 100644 index 000000000000..1230d4620530 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libawt/impl/Target_java_awt_image_ColorModel.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libawt.impl; + +import com.oracle.truffle.espresso.libs.libawt.LibAwt; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Substitution; + +@EspressoSubstitutions(type = "Ljava/awt/image/ColorModel;", group = LibAwt.class) +public final class Target_java_awt_image_ColorModel { + @Substitution + public static void initIDs() { + // nop + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libawt/impl/Target_java_awt_image_Raster.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libawt/impl/Target_java_awt_image_Raster.java new file mode 100644 index 000000000000..76ca0b58615f --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libawt/impl/Target_java_awt_image_Raster.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libawt.impl; + +import com.oracle.truffle.espresso.libs.libawt.LibAwt; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Substitution; + +@EspressoSubstitutions(type = "Ljava/awt/image/Raster;", group = LibAwt.class) +public final class Target_java_awt_image_Raster { + @Substitution + public static void initIDs() { + // nop + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libawt/impl/Target_java_awt_image_SampleModel.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libawt/impl/Target_java_awt_image_SampleModel.java new file mode 100644 index 000000000000..e8dce3c92c9c --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libawt/impl/Target_java_awt_image_SampleModel.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.libs.libawt.impl; + +import com.oracle.truffle.espresso.libs.libawt.LibAwt; +import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; +import com.oracle.truffle.espresso.substitutions.Substitution; + +@EspressoSubstitutions(type = "Ljava/awt/image/SampleModel;", group = LibAwt.class) +public final class Target_java_awt_image_SampleModel { + @Substitution + public static void initIDs() { + // nop + } +} From 894c1664696836d0b5581cc7e7410743023c13af Mon Sep 17 00:00:00 2001 From: Elia Trachsel Date: Tue, 22 Jul 2025 12:43:30 +0200 Subject: [PATCH 13/26] Introduction of NativeMemory to EspressoLibs: Basically just makes EspressoLibs use the newly added NativeMemory interface. Meaning all function that involved off-heap accesses were rewritten. Additionally the functionality to read and write from certain address in TruffleIO was added. The substitutions of Unsafe involving off-heap accesses were also adjusted to use NativeMemory. --- .../ffi/EspressoLibsNativeAccess.java | 24 +-- .../truffle/espresso/ffi/NativeAccess.java | 4 +- .../truffle/espresso/ffi/NoNativeAccess.java | 7 + .../memory/ByteArrayChunkedMemoryImpl.java | 42 +++- .../ffi/memory/ChunkedNativeMemory.java | 23 ++- .../espresso/ffi/memory/NativeMemory.java | 80 ++++++- .../ffi/memory/ProtoChunkedMemoryImpl.java | 75 ++++++- .../ffi/memory/UnsafeNativeMemory.java | 23 +++ .../ffi/nfi/NFIIsolatedNativeAccess.java | 4 +- .../espresso/ffi/nfi/NFINativeAccess.java | 2 +- .../truffle/espresso/ffi/nfi/NativeUtils.java | 27 --- .../oracle/truffle/espresso/io/TruffleIO.java | 195 ++++++++++++++---- .../impl/Target_java_lang_ClassLoader.java | 9 +- .../Target_sun_nio_ch_FileDispatcherImpl.java | 35 +--- .../Target_sun_nio_ch_TruffleDispatcher.java | 15 +- .../impl/Target_java_util_zip_Inflater.java | 77 +++++-- .../standard/Target_sun_misc_Unsafe.java | 80 ++++--- 17 files changed, 520 insertions(+), 202 deletions(-) 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 552ffb9649ab..55418359b770 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 @@ -25,6 +25,7 @@ import java.nio.file.Path; import java.util.Arrays; +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; @@ -32,6 +33,8 @@ 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.ffi.memory.ProtoChunkedMemoryImpl; import com.oracle.truffle.espresso.impl.ContextAccessImpl; import com.oracle.truffle.espresso.libs.Lib; import com.oracle.truffle.espresso.libs.Libs; @@ -49,6 +52,7 @@ * @see com.oracle.truffle.espresso.libs.Libs */ public class EspressoLibsNativeAccess extends ContextAccessImpl implements NativeAccess { + @CompilerDirectives.CompilationFinal protected NativeMemory nativeMemory = new ProtoChunkedMemoryImpl(); private static final TruffleLogger logger = TruffleLogger.getLogger(EspressoLanguage.ID, EspressoLibsNativeAccess.class); @@ -169,21 +173,6 @@ public SignatureCallNode createSignatureCall(NativeSignature nativeSignature) { return delegate.createSignatureCall(nativeSignature); } - @Override - public @Buffer TruffleObject allocateMemory(long size) { - return delegate.allocateMemory(size); - } - - @Override - public @Buffer TruffleObject reallocateMemory(@Pointer TruffleObject buffer, long newSize) { - return delegate.reallocateMemory(buffer, newSize); - } - - @Override - public void freeMemory(@Pointer TruffleObject buffer) { - delegate.freeMemory(buffer); - } - @Override public @Pointer TruffleObject createNativeClosure(TruffleObject executable, NativeSignature nativeSignature) { return delegate.createNativeClosure(executable, nativeSignature); @@ -194,6 +183,11 @@ public void prepareThread() { delegate.prepareThread(); } + @Override + public NativeMemory nativeMemory() { + return 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 2866a2f994d7..eb7096b8bbcb 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 @@ -26,7 +26,6 @@ import java.nio.file.Paths; import java.util.List; -import com.oracle.truffle.espresso.ffi.memory.NativeMemory; import org.graalvm.options.OptionValues; import com.oracle.truffle.api.CompilerDirectives; @@ -39,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; @@ -230,7 +230,7 @@ default boolean isBuiltIn(@SuppressWarnings("unused") String libname) { /** * 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 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 5bf7a62ec206..cf0f9eea547d 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 @@ -32,10 +32,12 @@ 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.meta.EspressoError; import com.oracle.truffle.espresso.substitutions.Collect; public class NoNativeAccess implements NativeAccess { + private static final TruffleLogger logger = TruffleLogger.getLogger(EspressoLanguage.ID, NoNativeAccess.class); @Override @@ -127,6 +129,11 @@ public void freeMemory(@Pointer TruffleObject buffer) { public void prepareThread() { } + @Override + public NativeMemory nativeMemory() { + return null; + } + private static TruffleLogger getLogger() { return logger; } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ByteArrayChunkedMemoryImpl.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ByteArrayChunkedMemoryImpl.java index 30678d7b61ec..e90d86a9a33a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ByteArrayChunkedMemoryImpl.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ByteArrayChunkedMemoryImpl.java @@ -1,3 +1,25 @@ +/* + * 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.util.Arrays; @@ -19,7 +41,7 @@ private static void validateAccess(int length, int byteIndex, int accessByteSize @Override public void setMemory(long address, long bytes, byte value) { byte[] chunk = getChunk(address); - validateAccess(chunk.length, Math.toIntExact(bytes), Byte.BYTES); + validateAccess(chunk.length, Math.toIntExact(bytes), 0); Arrays.fill(chunk, 0, Math.toIntExact(bytes), value); } @@ -31,7 +53,7 @@ public void putByte(long address, 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); + UNSAFE.putByteVolatile(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset, value); } } @@ -43,7 +65,7 @@ public void putShort(long address, 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); + UNSAFE.putShortVolatile(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset, value); } } @@ -55,7 +77,7 @@ public void putInt(long address, 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); + UNSAFE.putIntVolatile(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset, value); } } @@ -67,7 +89,7 @@ public byte getByte(long address, 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); + UNSAFE.getByteVolatile(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset); }; } @@ -79,7 +101,7 @@ public short getShort(long address, 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); + UNSAFE.getShortVolatile(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset); }; } @@ -91,7 +113,7 @@ public int getInt(long address, 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); + UNSAFE.getIntVolatile(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset); }; } @@ -103,7 +125,7 @@ public void putLong(long address, 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); + UNSAFE.putLongVolatile(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset, value); } } @@ -115,7 +137,7 @@ public double getDouble(long address, MemoryAccessMode accessMode) { 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); + UNSAFE.getDoubleVolatile(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset); }; } @@ -141,7 +163,7 @@ public long getLong(long address, 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); + UNSAFE.getLongVolatile(chunk, Unsafe.ARRAY_BYTE_BASE_OFFSET + chunkOffset); }; } 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 index 748833dd9270..1378a561a530 100644 --- 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 @@ -1,3 +1,25 @@ +/* + * 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.util.ArrayDeque; @@ -68,7 +90,6 @@ public synchronized void freeMemory(long address) { freeList.add(chunkIndex); } - public long reallocateMemory(long address, long bytes) { if (getChunkOffset(address) != 0) { throw new IllegalStateException("invalid address"); 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 index 72b4817c08ac..285fb4a5b5c1 100644 --- 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 @@ -1,9 +1,42 @@ +/* + * 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; + +/** + * The native memory abstraction layer for Espresso. It is assumed when you call + * {@link #allocateMemory(long)} it reserves a continuous block of memory of the specified size. Any + * location within the allocated block should be accessible by adding an offset (between 0 and size) + * to the base address. + */ public interface NativeMemory { long reallocateMemory(long address, long bytes); + long allocateMemory(long bytes); + void freeMemory(long bytes); + void setMemory(long address, long bytes, byte value); enum MemoryAccessMode { @@ -14,8 +47,11 @@ enum MemoryAccessMode { } void putByte(long address, byte value, MemoryAccessMode accessMode); + void putShort(long address, short value, MemoryAccessMode accessMode); + void putInt(long address, int value, MemoryAccessMode accessMode); + void putLong(long address, long value, MemoryAccessMode accessMode); default void putBoolean(long address, boolean value, MemoryAccessMode accessMode) { @@ -35,10 +71,12 @@ default void putDouble(long address, double value, MemoryAccessMode accessMode) } byte getByte(long address, MemoryAccessMode accessMode); + short getShort(long address, MemoryAccessMode accessMode); + int getInt(long address, MemoryAccessMode accessMode); - long getLong(long address, MemoryAccessMode accessMode); + long getLong(long address, MemoryAccessMode accessMode); default boolean getBoolean(long address, MemoryAccessMode accessMode) { return getByte(address, accessMode) != 0; @@ -57,6 +95,7 @@ default double getDouble(long address, MemoryAccessMode accessMode) { } boolean compareAndSetLong(long address, long expected, long newValue); + boolean compareAndSetInt(long address, int expected, int newValue); default long compareAndExchangeLong(long address, long expected, long newValue) { @@ -81,4 +120,43 @@ default int compareAndExchangeInt(long address, int expected, int newValue) { return previous; } + default void copyMemory(long srcBase, + long destBase, + long bytes, MemoryAccessMode accessMode) { + for (int offset = 0; offset < bytes; offset++) { + putByte(destBase + offset, getByte(srcBase + offset, accessMode), accessMode); + } + } + + default void readMemory(long addr, long bytes, ByteBuffer buf) { + for (long offset = 0; offset < bytes; offset++) { + buf.put(getByte(addr + offset, MemoryAccessMode.PLAIN)); + } + } + + default void readMemory(long addr, long bytes, byte[] buf) { + readMemory(addr, bytes, ByteBuffer.wrap(buf)); + } + + default void writeMemory(long addr, long bytes, ByteBuffer buf) { + for (long offset = 0; offset < bytes; offset++) { + putByte(addr + offset, buf.get(), MemoryAccessMode.PLAIN); + } + } + + default void writeMemory(long addr, long bytes, byte[] buf) { + writeMemory(addr, bytes, ByteBuffer.wrap(buf)); + } + + /* + * Should be overwritten if a direct way to access Memory can be provided which does not involve + * copying. + */ + default ByteBuffer getDirectBuffer(long address, long bytes) { + throw new UnsupportedOperationException("DirectMemory Access is not supported"); + } + + default boolean isDirectBufferSupported() { + return false; + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ProtoChunkedMemoryImpl.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ProtoChunkedMemoryImpl.java index 6898effddbef..f9e0fd18d416 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ProtoChunkedMemoryImpl.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ProtoChunkedMemoryImpl.java @@ -1,18 +1,38 @@ +/* + * 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 java.util.Arrays; import com.oracle.truffle.api.memory.ByteArraySupport; -import com.oracle.truffle.espresso.vm.UnsafeAccess; - -import sun.misc.Unsafe; public class ProtoChunkedMemoryImpl extends ChunkedNativeMemory { private static final ByteArraySupport BYTES = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN - ? ByteArraySupport.littleEndian() - : ByteArraySupport.bigEndian(); + ? ByteArraySupport.littleEndian() + : ByteArraySupport.bigEndian(); private static void validateAccess(int length, int byteIndex, int accessByteSize) { if (byteIndex < 0 || byteIndex > length - accessByteSize) { @@ -20,10 +40,19 @@ private static void validateAccess(int length, int byteIndex, int accessByteSize } } + @Override + public void copyMemory(long srcBase, + long destBase, + long bytes, MemoryAccessMode accessMode) { + copyBytes(srcBase, destBase, bytes); + } + @Override public void setMemory(long address, long bytes, byte value) { byte[] chunk = getChunk(address); - validateAccess(chunk.length, Math.toIntExact(bytes), Byte.BYTES); + // I think the accessByteSize should be 0 instead of 1 as we access exactly bytes many bytes + // in chunk + validateAccess(chunk.length, Math.toIntExact(bytes), 0); Arrays.fill(chunk, 0, Math.toIntExact(bytes), value); } @@ -34,7 +63,7 @@ public void putBoolean(long address, boolean value, MemoryAccessMode accessMode) switch (accessMode) { case PLAIN -> BYTES.putByte(chunk, chunkOffset, value ? (byte) 1 : (byte) 0); case OPAQUE, RELEASE_ACQUIRE, VOLATILE -> - BYTES.putByteVolatile(chunk, chunkOffset, value ? (byte) 1 : (byte) 0); + BYTES.putByteVolatile(chunk, chunkOffset, value ? (byte) 1 : (byte) 0); } } @@ -227,6 +256,33 @@ protected long getChunkSize(long address) { return getChunk(address).length; } + @Override + public ByteBuffer getDirectBuffer(long address, long bytes) { + int intByteSize = Math.toIntExact(bytes); + byte[] fromChunk = getChunk(address); + int fromOffset = Math.toIntExact(getChunkOffset(address)); + validateAccess(fromChunk.length, fromOffset, intByteSize); + return ByteBuffer.wrap(fromChunk, fromOffset, intByteSize); + } + + @Override + public void writeMemory(long address, long bytes, ByteBuffer buf) { + int intByteSize = Math.toIntExact(bytes); + int fromOffset = Math.toIntExact(getChunkOffset(address)); + byte[] fromChunk = getChunk(address); + validateAccess(fromChunk.length, fromOffset, intByteSize); + buf.get(fromChunk, fromOffset, intByteSize); + } + + @Override + public void readMemory(long address, long bytes, ByteBuffer buf) { + int intByteSize = Math.toIntExact(bytes); + int fromOffset = Math.toIntExact(getChunkOffset(address)); + byte[] fromChunk = getChunk(address); + validateAccess(fromChunk.length, fromOffset, intByteSize); + buf.put(fromChunk, fromOffset, intByteSize); + } + @Override protected void copyBytes(long fromAddress, long toAddress, long byteSize) { int intByteSize = Math.toIntExact(byteSize); @@ -241,4 +297,9 @@ protected void copyBytes(long fromAddress, long toAddress, long byteSize) { System.arraycopy(fromChunk, fromOffset, toChunk, toOffset, intByteSize); } + + @Override + public boolean isDirectBufferSupported() { + return true; + } } 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 index 51c82d1f024b..7074cc9a7e5c 100644 --- 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 @@ -1,6 +1,29 @@ +/* + * 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.vm.UnsafeAccess; + import sun.misc.Unsafe; public class UnsafeNativeMemory implements NativeMemory { 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 4aa5e477f289..8ee8e5ba839e 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 @@ -46,7 +46,6 @@ 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; @@ -100,7 +99,8 @@ public final class NFIIsolatedNativeAccess extends NFINativeAccess { @Override public long allocateMemory(long bytes) { try { - @Pointer TruffleObject address = (TruffleObject) UNCACHED_INTEROP.execute(malloc, bytes); + @Pointer + TruffleObject address = (TruffleObject) UNCACHED_INTEROP.execute(malloc, bytes); if (UNCACHED_INTEROP.isNull(address)) { // malloc returned NULL return 0L; 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 c1aac37d12d2..51214ac578ab 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 @@ -54,13 +54,13 @@ 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.memory.NativeMemory; 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; 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 0176ea630258..311c6d5e63d8 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/nfi/NativeUtils.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/nfi/NativeUtils.java @@ -22,8 +22,6 @@ */ package com.oracle.truffle.espresso.ffi.nfi; -import static com.oracle.truffle.espresso.substitutions.standard.Target_sun_misc_Unsafe.ADDRESS_SIZE; - import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -195,29 +193,4 @@ public static String fromUTF8Ptr(long rawBytesPtr) { public static ByteBuffer allocateDirect(int capacity) { return ByteBuffer.allocateDirect(capacity).order(ByteOrder.nativeOrder()); } - - @TruffleBoundary - public static ByteBuffer[] getByteBuffersFromIOVec(long address, int len) { - // constants - int lenOffset = ADDRESS_SIZE; - int sizeOfIOVec = (short) (ADDRESS_SIZE * 2); - int baseOffset = 0; - - long curIOVecAddr = address; - long nextAddr; - long nextLen; - ByteBuffer[] buffs = new ByteBuffer[len]; - for (int i = 0; i < len; i++) { - if (ADDRESS_SIZE == 4) { - nextAddr = UNSAFE.getInt(curIOVecAddr + baseOffset); - nextLen = UNSAFE.getInt(curIOVecAddr + lenOffset); - } else { - nextAddr = UNSAFE.getLong(curIOVecAddr + baseOffset); - nextLen = UNSAFE.getLong(curIOVecAddr + lenOffset); - } - buffs[i] = directByteBuffer(nextAddr, nextLen); - curIOVecAddr += sizeOfIOVec; - } - return buffs; - } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/TruffleIO.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/io/TruffleIO.java index 6c2ea100769c..7b6a921c9142 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 @@ -23,6 +23,7 @@ package com.oracle.truffle.espresso.io; 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; @@ -78,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; @@ -656,7 +658,7 @@ public boolean close(@JavaType(Object.class) StaticObject self, /** * Obtains the length of the file associated with the given file descriptor holder. - * + * * @see RandomAccessFile#length() */ @TruffleBoundary @@ -697,6 +699,31 @@ public int writeBytes(@JavaType(Object.class) StaticObject self, } } + /** + * Writes bytes from the address specified to the file associated with the given file descriptor + * holder. + * + * @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. + * @see java.io.FileOutputStream#write(byte[]) + */ + @TruffleBoundary + public int writeAddress(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess, + long address, int length) { + NativeMemory nativeMemory = context.getNativeAccess().nativeMemory(); + if (nativeMemory.isDirectBufferSupported()) { + return writeBytes(self, fdAccess, nativeMemory.getDirectBuffer(address, length)); + } else { + byte[] buf = new byte[length]; + nativeMemory.readMemory(address, length, buf); + return writeBytes(self, fdAccess, buf, 0, length); + } + } + /** * Writes buffered bytes to the file associated with the given file descriptor holder. * @@ -727,66 +754,65 @@ 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()); - if (channel instanceof GatheringByteChannel gatheringByteChannel) { - try { - return gatheringByteChannel.write(buffers); - } 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); + long address, int length) { + if (context.getNativeAccess().nativeMemory().isDirectBufferSupported()) { + // Extracting all the buffers comes at a cost. In my opinions it is only worth it if we + // have direct memory access. Otherwise, we need to copy alot between buffers and memory + StaticObject fileDesc = getFileDesc(self, fdAccess); + Channel channel = Checks.ensureOpen(getChannel(getFD(fileDesc)), getContext()); + if (channel instanceof GatheringByteChannel gatheringByteChannel) { + try { + return gatheringByteChannel.write(getByteBuffersFromIOVec(address, length, context.getNativeAccess().nativeMemory())); + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } } - return ret; } + return sequentialWritev(self, fdAccess, address, length); } /** - * 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) { - StaticObject fileDesc = getFileDesc(self, fdAccess); - Channel channel = Checks.ensureOpen(getChannel(getFD(fileDesc)), getContext()); - if (channel instanceof ScatteringByteChannel scatteringByteChannel) { - try { - return scatteringByteChannel.read(buffers); - } 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); + long address, int length) { + if (context.getNativeAccess().nativeMemory().isDirectBufferSupported()) { + StaticObject fileDesc = getFileDesc(self, fdAccess); + Channel channel = Checks.ensureOpen(getChannel(getFD(fileDesc)), getContext()); + if (channel instanceof ScatteringByteChannel scatteringByteChannel) { + try { + return scatteringByteChannel.read(getByteBuffersFromIOVec(address, length, context.getNativeAccess().nativeMemory())); + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } } - return ret; } + return sequentialReadv(self, fdAccess, address, length); } /** @@ -885,6 +911,28 @@ public int readBytes(int fd, return readBytesImpl(getReadableChannel(fd), bytes, off, len, context); } + /** + * Reads a byte sequence from the file associated with the given file descriptor. + * + * @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) { + NativeMemory nativeMemory = context.getNativeAccess().nativeMemory(); + if (nativeMemory.isDirectBufferSupported()) { + return readBytes(self, fdAccess, nativeMemory.getDirectBuffer(addr, length)); + } else { + byte[] buf = new byte[length]; + int ret = readBytes(self, fdAccess, buf, 0, length); + nativeMemory.writeMemory(addr, length, buf); + return ret; + } + } + /** * 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. @@ -1298,6 +1346,77 @@ private static int readBytesImpl(ReadableByteChannel readableChannel, byte[] b, } } + private long sequentialWritev(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess, long address, int len) { + // constants + int lenOffset = ADDRESS_SIZE; + int sizeOfIOVec = (short) (ADDRESS_SIZE * 2); + long curIOVecAddr = address; + long nextAddr; + long nextLen; + long ret = 0; + NativeMemory nativeMemory = context.getNativeAccess().nativeMemory(); + for (int i = 0; i < len; i++) { + if (ADDRESS_SIZE == 4) { + nextAddr = nativeMemory.getInt(curIOVecAddr, NativeMemory.MemoryAccessMode.PLAIN); + nextLen = nativeMemory.getInt(curIOVecAddr + lenOffset, NativeMemory.MemoryAccessMode.PLAIN); + } else { + nextAddr = nativeMemory.getLong(curIOVecAddr, NativeMemory.MemoryAccessMode.PLAIN); + nextLen = nativeMemory.getLong(curIOVecAddr + lenOffset, NativeMemory.MemoryAccessMode.PLAIN); + } + ret += writeAddress(self, fdAccess, nextAddr, Math.toIntExact(nextLen)); + curIOVecAddr += sizeOfIOVec; + } + return ret; + } + + @TruffleBoundary + private static ByteBuffer[] getByteBuffersFromIOVec(long address, int len, NativeMemory nativeMemory) { + // constants + int lenOffset = ADDRESS_SIZE; + int sizeOfIOVec = (short) (ADDRESS_SIZE * 2); + long curIOVecAddr = address; + long nextAddr; + long nextLen; + ByteBuffer[] buffs = new ByteBuffer[len]; + for (int i = 0; i < len; i++) { + if (ADDRESS_SIZE == 4) { + nextAddr = nativeMemory.getInt(curIOVecAddr, NativeMemory.MemoryAccessMode.PLAIN); + nextLen = nativeMemory.getInt(curIOVecAddr + lenOffset, NativeMemory.MemoryAccessMode.PLAIN); + } else { + nextAddr = nativeMemory.getLong(curIOVecAddr, NativeMemory.MemoryAccessMode.PLAIN); + nextLen = nativeMemory.getLong(curIOVecAddr + lenOffset, NativeMemory.MemoryAccessMode.PLAIN); + } + buffs[i] = nativeMemory.getDirectBuffer(nextAddr, nextLen); + curIOVecAddr += sizeOfIOVec; + } + return buffs; + } + + private long sequentialReadv(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess, long address, int length) { + // constants + int lenOffset = ADDRESS_SIZE; + int sizeOfIOVec = (short) (ADDRESS_SIZE * 2); + long curIOVecAddr = address; + long nextAddr; + long nextLen; + long ret = 0; + NativeMemory nativeMemory = context.getNativeAccess().nativeMemory(); + for (int i = 0; i < length; i++) { + if (ADDRESS_SIZE == 4) { + nextAddr = nativeMemory.getInt(curIOVecAddr, NativeMemory.MemoryAccessMode.PLAIN); + nextLen = nativeMemory.getInt(curIOVecAddr + lenOffset, NativeMemory.MemoryAccessMode.PLAIN); + } else { + nextAddr = nativeMemory.getLong(curIOVecAddr, NativeMemory.MemoryAccessMode.PLAIN); + nextLen = nativeMemory.getLong(curIOVecAddr + lenOffset, NativeMemory.MemoryAccessMode.PLAIN); + } + ret += readAddress(self, fdAccess, nextAddr, Math.toIntExact(nextLen)); + curIOVecAddr += sizeOfIOVec; + } + return ret; + } + private ReadableByteChannel getReadableChannel(@JavaType(Object.class) StaticObject self, FDAccess fdAccess) { return getReadableChannel(getFD(getFileDesc(self, fdAccess))); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_ClassLoader.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_ClassLoader.java index 686e820f515e..db0e1ef0560b 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 @@ -159,17 +159,16 @@ public static void registerNatives() { // retrieve the GuestBuffer as Array @Pointer TruffleObject dataAddrPointer = ctx.getJNI().GetDirectBufferAddress(data); - ByteBuffer dataBuffer = NativeUtils.directByteBuffer(dataAddrPointer, len + off); + long rawDataPtr = NativeUtils.interopAsPointer(dataAddrPointer); + byte[] buf = new byte[len]; + // reads the memory in the buffer + ctx.getNativeAccess().nativeMemory().readMemory(rawDataPtr + off, len, buf); 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 56eeefaa3122..504d9027c67e 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 @@ -24,21 +24,14 @@ 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.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; @@ -73,25 +66,14 @@ public static long map0(@JavaType(FileDescriptor.class) StaticObject fd, int pro // sun.nio.ch.FileChannelImpl.toProt throw Throw.throwUnsupported("mmap for public writes is not supported at the moment", ctx); } - @Buffer - TruffleObject buffer = ctx.getNativeAccess().allocateMemory(length); - long addr; - try { - addr = InteropLibrary.getUncached().asPointer(buffer); - } catch (UnsupportedMessageException e) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw EspressoError.shouldNotReachHere(e); - } + NativeMemory nativeMemory = ctx.getNativeAccess().nativeMemory(); + long addr = nativeMemory.allocateMemory(length); 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); - io.readBytes(fd, FDAccess.forFileDescriptor(), byteBuffer); + io.readAddress(fd, FDAccess.forFileDescriptor(), addr, Math.toIntExact(length)); } finally { - // always reset the position io.seek(fd, FDAccess.forFileDescriptor(), oldPosition); } return addr; @@ -170,8 +152,7 @@ public static long size0(@JavaType(FileDescriptor.class) StaticObject fd, @Injec @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); + return io.readAddress(fd, FDAccess.forFileDescriptor(), address, len); } @@ -185,8 +166,7 @@ public static int readv0(@JavaType(FileDescriptor.class) StaticObject fd, long a @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); + return io.writeAddress(fd, FDAccess.forFileDescriptor(), address, len); } @Substitution @@ -208,11 +188,10 @@ public static void close0(@JavaType(FileDescriptor.class) StaticObject fd, @Inje 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); + 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 9e67855c31dc..c027bab7f0ef 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,26 @@ 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); + return io.readAddress(fd, FDAccess.forFileDescriptor(), address, len); } @Substitution @Throws(IOException.class) public static int readv0(@JavaType(FileDescriptor.class) StaticObject fd, long address, int len, @Inject TruffleIO io) { - ByteBuffer[] buffers = NativeUtils.getByteBuffersFromIOVec(address, len); - return Math.toIntExact(io.readByteBuffers(fd, FDAccess.forFileDescriptor(), buffers)); - + return Math.toIntExact(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); + return io.writeAddress(fd, FDAccess.forFileDescriptor(), address, len); } @Substitution @Throws(IOException.class) public static int 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 Math.toIntExact(io.writeByteBuffers(fd, FDAccess.forFileDescriptor(), buffers)); + return Math.toIntExact(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 9b396b48ab3e..2f1ded8c0660 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,17 @@ */ package com.oracle.truffle.espresso.libs.libzip.impl; -import java.nio.ByteBuffer; 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.ffi.memory.NativeMemory; +import com.oracle.truffle.espresso.io.TruffleIO; 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.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 +74,16 @@ 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) { + NativeMemory nativeMemory = ctx.getNativeAccess().nativeMemory(); + if (nativeMemory.isDirectBufferSupported()) { + libsState.getInflater(addr).setDictionary(nativeMemory.getDirectBuffer(bufAddress, len)); + } else { + byte[] buff = new byte[len]; + nativeMemory.readMemory(bufAddress, len, buff); + libsState.getInflater(addr).setDictionary(buff, 0, len); + } } @Substitution(hasReceiver = true) @@ -94,6 +102,8 @@ public static long inflateBytesBytes(@JavaType(Inflater.class) StaticObject gues long bytesReadOld = hostInflater.getBytesRead(); long bytesWrittenOld = hostInflater.getBytesWritten(); try { + // do inflate and encode result + outputByteArray = outputArray.unwrap(language); int written = hostInflater.inflate(outputByteArray, outputOff, outputLen); int read = Math.toIntExact(hostInflater.getBytesRead() - bytesReadOld); return encodeResult(read, written, hostInflater); @@ -106,10 +116,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,7 +128,8 @@ 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 = inflateFromAddress(outputAddress, outputLen, hostInflater, ctx); int read = Math.toIntExact(hostInflater.getBytesRead() - bytesReadOld); return encodeResult(read, written, hostInflater); } catch (DataFormatException e) { @@ -130,19 +142,18 @@ 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 Inflater hostInflater = libsState.getInflater(addr); - hostInflater.setInput(inputByteBuffer); + setInputFromAddress(inputAddress, inputLen, hostInflater, ctx); // cache bytes/read/written for the exception case long bytesReadOld = hostInflater.getBytesRead(); long bytesWrittenOld = hostInflater.getBytesWritten(); try { + // do inflate and encode result + byte[] outputByteArray = outputArray.unwrap(language); int written = hostInflater.inflate(outputByteArray, outputOff, outputLen); int read = Math.toIntExact(hostInflater.getBytesRead() - bytesReadOld); return encodeResult(read, written, hostInflater); @@ -157,19 +168,17 @@ 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); + @Inject LibsState libsState, @Inject LibsMeta libsMeta, @Inject EspressoLanguage language, + @Inject EspressoContext ctx) { // get host Inflater and set Input Inflater hostInflater = libsState.getInflater(addr); - hostInflater.setInput(inputByteBuffer); + setInputFromAddress(inputAddress, inputLen, hostInflater, ctx); // 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 + int written = inflateFromAddress(outputAddress, outputLen, hostInflater, ctx); int read = Math.toIntExact(hostInflater.getBytesRead() - bytesReadOld); return encodeResult(read, written, hostInflater); } catch (DataFormatException e) { @@ -227,4 +236,28 @@ private static long encodeResult(int read, int written, Inflater hostInflater) { return result; } + + private static void setInputFromAddress(long addr, int len, Inflater hostInflater, EspressoContext ctx) { + NativeMemory nativeMemory = ctx.getNativeAccess().nativeMemory(); + if (nativeMemory.isDirectBufferSupported()) { + hostInflater.setInput(nativeMemory.getDirectBuffer(addr, len)); + } else { + byte[] buff = new byte[len]; + nativeMemory.readMemory(addr, len, buff); + hostInflater.setInput(buff); + } + } + + private static int inflateFromAddress(long addr, int len, Inflater hostInflater, EspressoContext ctx) throws DataFormatException { + NativeMemory nativeMemory = ctx.getNativeAccess().nativeMemory(); + int written = 0; + if (nativeMemory.isDirectBufferSupported()) { + written = hostInflater.inflate(nativeMemory.getDirectBuffer(addr, len)); + } else { + byte[] outputBuff = new byte[len]; + written = hostInflater.inflate(outputBuff); + nativeMemory.writeMemory(addr, len, outputBuff); + } + return written; + } } 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 51bc5e6144b4..b1ae3a689423 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 @@ -26,7 +26,6 @@ import static com.oracle.truffle.espresso.threads.ThreadState.PARKED; import static com.oracle.truffle.espresso.threads.ThreadState.TIMED_PARKED; -import java.lang.invoke.VarHandle; import java.lang.reflect.Array; import java.nio.ByteOrder; import java.security.ProtectionDomain; @@ -41,9 +40,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; @@ -53,8 +49,6 @@ 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; @@ -90,7 +84,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; @@ -444,9 +440,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 */ @@ -475,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 @@ -529,11 +521,33 @@ 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 destBase, long destOffset, 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 dest = MetaUtil.unwrapArrayOrNull(language, destBase); + NativeMemory nativeMemory = context.getNativeAccess().nativeMemory(); + if (src == null && dest == null) { + nativeMemory.copyMemory(srcOffset, destOffset, bytes, MemoryAccessMode.VOLATILE); + return; + } + Unsafe unsafe = UnsafeAccess.getIfAllowed(meta); + int byteArrayBaseOffset = unsafe.arrayBaseOffset(byte[].class); + if (src == null) { + byte[] srcArray = new byte[Math.toIntExact(bytes)]; + nativeMemory.readMemory(srcOffset, bytes, srcArray); + unsafe.copyMemory(srcArray, byteArrayBaseOffset, dest, destOffset, bytes); + return; + } + if (dest == null) { + byte[] buff = new byte[Math.toIntExact(bytes)]; + unsafe.copyMemory(src, srcOffset, buff, byteArrayBaseOffset, bytes); + nativeMemory.writeMemory(destOffset, bytes, buff); + return; + } + unsafe.copyMemory(src, srcOffset, dest, destOffset, bytes); } @Substitution(hasReceiver = true) @@ -578,17 +592,19 @@ 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; + ctx.getNativeAccess().nativeMemory().setMemory(offset, bytes, value); } else if (o.getKlass().isArray()) { + hostObject = o.unwrap(language); + UnsafeAccess.getIfAllowed(meta).setMemory(hostObject, offset, bytes, value); } else { CompilerDirectives.transferToInterpreterAndInvalidate(); throw EspressoError.shouldNotReachHere(); } - UnsafeAccess.getIfAllowed(meta).setMemory(hostObject, offset, bytes, value); } /** @@ -756,7 +772,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) @@ -853,7 +868,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) @@ -999,7 +1013,9 @@ void doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject s } } - /** @see GetByteWithBase */ + /** + * @see GetByteWithBase + */ @GenerateInline(false) // not available in substitutions @Substitution(hasReceiver = true) abstract static class PutInt extends UnsafeAccessNode { @@ -1560,7 +1576,7 @@ void doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject sel @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) { + @JavaType(Object.class) StaticObject value) { UnsafeAccess.getIfAllowed(getMeta()).putOrderedObject(unwrapNullOrArray(getLanguage(), holder), offset, value); } @@ -1666,7 +1682,7 @@ StaticObject doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticOb @Specialization(guards = "isArray(holder)") @JavaType(Object.class) StaticObject doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, - long offset) { + long offset) { return (StaticObject) UnsafeAccess.getIfAllowed(getMeta()).getObject(unwrapNullOrArray(getLanguage(), holder), offset); } @@ -1811,7 +1827,6 @@ short doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject s return UnsafeAccess.getIfAllowed(getMeta()).getShort(unwrapArray(getLanguage(), holder), offset); } - @Specialization(guards = "!isNullOrArray(holder)") static short doGeneric(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, @Bind Node node, @@ -2098,7 +2113,7 @@ StaticObject doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticOb @Specialization(guards = "isArray(holder)") @JavaType(Object.class) StaticObject doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, - long offset) { + long offset) { return (StaticObject) UnsafeAccess.getIfAllowed(getMeta()).getObjectVolatile(unwrapArray(getLanguage(), holder), offset); } @@ -2291,7 +2306,6 @@ int doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject sel return UnsafeAccess.getIfAllowed(getMeta()).getIntVolatile(unwrapArray(getLanguage(), holder), offset); } - @Specialization(guards = "!isNullOrArray(holder)") static int doGeneric(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, @Bind Node node, @@ -2504,7 +2518,9 @@ short doCached(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject } } - /** @see GetByteWithBase */ + /** + * @see GetByteWithBase + */ @GenerateInline(false) // not available in substitutions @Substitution(hasReceiver = true) abstract static class GetInt extends UnsafeAccessNode { @@ -2616,7 +2632,7 @@ void doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject sel @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) { + @JavaType(Object.class) StaticObject value) { UnsafeAccess.getIfAllowed(getMeta()).putObjectVolatile(unwrapArray(getLanguage(), holder), offset, value); } @@ -2976,7 +2992,7 @@ boolean doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject @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) { + @JavaType(Object.class) StaticObject before, @JavaType(Object.class) StaticObject after) { return UnsafeAccess.getIfAllowed(getMeta()).compareAndSwapObject(unwrapArray(getLanguage(), holder), offset, before, after); } @@ -3024,7 +3040,7 @@ boolean doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject @Specialization(guards = "isArray(holder)") boolean doArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, - int before, int after) { + int before, int after) { return UnsafeAccess.getIfAllowed(getMeta()).compareAndSwapInt(unwrapArray(getLanguage(), holder), offset, before, after); } @@ -3074,7 +3090,7 @@ abstract boolean execute(@JavaType(Unsafe.class) StaticObject self, @JavaType(Ob @Specialization(guards = "isNull(holder)") boolean doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, - long before, long after) { + long before, long after) { return getNativeAccess().nativeMemory().compareAndSetLong(offset, before, after); } @@ -3154,8 +3170,8 @@ StaticObject doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticOb @Specialization(guards = "isArray(holder)") @JavaType(Object.class) 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) { + long offset, + @JavaType(Object.class) StaticObject before, @JavaType(Object.class) StaticObject after) { UnsafeAccess.checkAllowed(getMeta()); return (StaticObject) UnsafeSupport.compareAndExchangeObject(unwrapArray(getLanguage(), holder), offset, before, after); } @@ -3209,7 +3225,7 @@ int doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self @Specialization(guards = "isArray(holder)") int doNullOrArray(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, - int before, int after) { + int before, int after) { return UnsafeSupport.compareAndExchangeInt(unwrapArray(getLanguage(), holder), offset, before, after); } From 0c7d3c6ca21f53adc391439b531b536c93024a6a Mon Sep 17 00:00:00 2001 From: Simon Gruenbacher Date: Wed, 23 Jul 2025 08:57:39 +0200 Subject: [PATCH 14/26] re-apply changes from PR 20085 --- .../src/com/oracle/svm/truffle/TruffleBaseFeature.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java index 85cae200402c..43a75f1e1fae 100644 --- a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java +++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java @@ -1227,7 +1227,9 @@ private static Class loadClass(String name) { private static final class StaticObjectPodBasedSupport { static void onBuildInvocation(Class storageSuperClass, Class factoryInterface) { - PodSupport.singleton().registerSuperclass(storageSuperClass, factoryInterface); + if(PodSupport.isPresent()) { + PodSupport.singleton().registerSuperclass(storageSuperClass, factoryInterface); + } } } From 446f1014333027beebba1acb2d0921ac887c128d Mon Sep 17 00:00:00 2001 From: Simon Gruenbacher Date: Fri, 1 Aug 2025 08:57:58 +0200 Subject: [PATCH 15/26] able to compile and run HelloWorld --- .../Target_java_lang_SecurityManager.java | 2 +- .../libjava/impl/Target_java_lang_System.java | 11 ++++++ .../espresso/runtime/EspressoProperties.java | 6 +++- .../standard/Target_java_lang_Module.java | 25 +++++++++++++ .../oracle/truffle/api/InternalResource.java | 1 + .../system/WebImageIOSubstitutions.java | 36 ++++++++++++++++++- .../system/WebImageJavaLangSubstitutions.java | 2 ++ 7 files changed, 80 insertions(+), 3 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_SecurityManager.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_SecurityManager.java index f637f17781f9..730bd7904cae 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_SecurityManager.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_SecurityManager.java @@ -43,7 +43,7 @@ public final class Target_java_lang_SecurityManager { @SuppressWarnings("deprecated") @Substitution(hasReceiver = true, languageFilter = VersionFilter.Java21.class) public static @JavaType(Class[].class) StaticObject getClassContext(@JavaType(SecurityManager.class) StaticObject self, @Inject VM vm, @Inject LibsMeta libsMeta, @Inject EspressoContext context) { - if (!libsMeta.java_lang_SecurityManager_initialized.getBoolean(self)) { + if (libsMeta.java_lang_SecurityManager_initialized != null && !libsMeta.java_lang_SecurityManager_initialized.getBoolean(self)) { // TODO: How to deal with java version stuff? throw Throw.throwSecurityException("security manager not initialized", context); } return vm.JVM_GetClassContext(); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_System.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_System.java index 913161bccb01..095403d94997 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_System.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_System.java @@ -96,6 +96,9 @@ public static final class Raw { } known[props.stdoutEncodingNdx] = java.lang.System.getProperty("stdout.encoding"); known[props.stderrEncodingNdx] = java.lang.System.getProperty("stderr.encoding"); + if(ctx.getJavaVersion().java25OrLater()) { + known[props.stdinEncodingNdx] = java.lang.System.getProperty("stdin.encoding"); + } known[props.osNameNdx] = java.lang.System.getProperty("os.name"); known[props.osArchNdx] = java.lang.System.getProperty("os.arch"); @@ -176,6 +179,7 @@ private static final class Props { private final int socksProxyHostNdx; private final int socksProxyPortNdx; private final int stderrEncodingNdx; + private final int stdinEncodingNdx; private final int stdoutEncodingNdx; private final int sunArchAbiNdx; private final int sunArchDataModelNdx; @@ -228,6 +232,13 @@ private Props(EspressoContext ctx) { socksProxyHostNdx = guestRaw.lookupDeclaredField(ctx.getNames().getOrCreate("_socksProxyHost_NDX"), Types._int).getInt(guestRaw.tryInitializeAndGetStatics()); socksProxyPortNdx = guestRaw.lookupDeclaredField(ctx.getNames().getOrCreate("_socksProxyPort_NDX"), Types._int).getInt(guestRaw.tryInitializeAndGetStatics()); stderrEncodingNdx = guestRaw.lookupDeclaredField(ctx.getNames().getOrCreate("_stderr_encoding_NDX"), Types._int).getInt(guestRaw.tryInitializeAndGetStatics()); + + if(ctx.getJavaVersion().java25OrLater()) { // https://github.com/openjdk/valhalla/commit/15f42e348d4068bd90dd75b270a372353fe0ed07#diff-0b761d753142789aae9ff9bb970787ee6ece0df8f85852df461edc5b2e13f875 + stdinEncodingNdx = guestRaw.lookupDeclaredField(ctx.getNames().getOrCreate("_stdin_encoding_NDX"), Types._int).getInt(guestRaw.tryInitializeAndGetStatics()); + } else { + stdinEncodingNdx = -1; + } + stdoutEncodingNdx = guestRaw.lookupDeclaredField(ctx.getNames().getOrCreate("_stdout_encoding_NDX"), Types._int).getInt(guestRaw.tryInitializeAndGetStatics()); sunArchAbiNdx = guestRaw.lookupDeclaredField(ctx.getNames().getOrCreate("_sun_arch_abi_NDX"), Types._int).getInt(guestRaw.tryInitializeAndGetStatics()); sunArchDataModelNdx = guestRaw.lookupDeclaredField(ctx.getNames().getOrCreate("_sun_arch_data_model_NDX"), Types._int).getInt(guestRaw.tryInitializeAndGetStatics()); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoProperties.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoProperties.java index 2dc610352c2e..af6748380db4 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoProperties.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoProperties.java @@ -381,7 +381,11 @@ List defaultBootClasspath() { } path = javaHome().resolve(MODULES_IMAGE); if (Files.isReadable(path)) { - bootClassPathVersion(BootClassPathType.IMAGE); + // if(Files.isDirectory(path)) { // TODO: check in into seperate PR maybe? + // bootClassPathVersion(BootClassPathType.EXPLODED); + // } else { + bootClassPathVersion(BootClassPathType.IMAGE); + // } List paths = new ArrayList<>(1); paths.add(path); return paths; diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_lang_Module.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_lang_Module.java index 67d799b72d1b..420e278ab705 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_lang_Module.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_lang_Module.java @@ -26,7 +26,9 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.espresso.EspressoLanguage; +import com.oracle.truffle.espresso.libs.libjava.LibJava; 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; @@ -43,6 +45,29 @@ public final class Target_java_lang_Module { * the native linking of these methods to their 'JVM_*' counterparts. */ + /* + @Substitution(hasReceiver = true) + @TruffleBoundary + @JavaType(internalName = "Ljava/lang/Module;") static StaticObject implAddEnableNativeAccess(@JavaType(internalName = "Ljava/lang/Module;") StaticObject self) { + // no-op (TODO: add some sort of webimage context filter) + return self; + } + + */ + + @EspressoSubstitutions(type = "Ljava/lang/Module$EnableNativeAccess;") + public static final class EnableNativeAccess { + @Substitution + public static boolean isNativeAccessEnabled(@JavaType(internalName = "Ljava/lang/Module;") StaticObject target) { + return true; // TODO + } + + @Substitution + public static boolean trySetEnableNativeAccess(@JavaType(internalName = "Ljava/lang/Module;") StaticObject target) { + return true; // TODO + } + } + @Substitution @TruffleBoundary public static void addExports0(@JavaType(internalName = "Ljava/lang/Module;") StaticObject from, diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/InternalResource.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/InternalResource.java index e21c0052e354..c8f9b0a31233 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/InternalResource.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/InternalResource.java @@ -279,6 +279,7 @@ public void unpackResourceFiles(Path source, Path target, Path relativizeTo, Pre Properties fileList = loadFileList(source); for (var e : fileList.entrySet()) { Path resource = Path.of((String) e.getKey()); + if(resource.getFileName().endsWith(".DS_Store")) continue; // resolves 'could not find metadata file' problem on mac; DO NOT CHECK IN Set attrs = parseAttrs((String) e.getValue()); if (resource.isAbsolute()) { throw new IllegalArgumentException("The file list must contain only relative paths, but the absolute path " + resource + " was given."); diff --git a/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/system/WebImageIOSubstitutions.java b/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/system/WebImageIOSubstitutions.java index 16eafde4ec4a..a340401d4670 100644 --- a/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/system/WebImageIOSubstitutions.java +++ b/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/system/WebImageIOSubstitutions.java @@ -32,6 +32,13 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.DoubleBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.LongBuffer; +import java.nio.MappedByteBuffer; +import java.nio.ShortBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.LinkOption; @@ -92,6 +99,24 @@ public File toFile() { } } +/* +@TargetClass(className = "org.graalvm.shadowed.com.google.common.jimfs.JimfsFileChannel") +final class Target_org_graalvm_shadowed_com_google_common_jimfs_JimfsFileChannel { + + + // Note: This needs to be a subclass IOException so that BasicImageReader in Espresso fails gracefully. + // In jimfs this would be an UnsupportedOperationException, which is not caught in Espresso. + // The operation is not supported because MappedByteBuffer is an abstract class inheriting from the sealed class Buffer, + // and thus cannot be implemented. + + + @Substitute + public MappedByteBuffer map(FileChannel.MapMode mode, long position, long size) throws IOException { + throw new IOException("cannot map jimfs file"); + } +} +*/ + @TargetClass(java.io.FileInputStream.class) final class Target_java_io_FileInputStream_Web { @@ -275,7 +300,16 @@ private static FileSystemProvider getDefaultProvider() { } } - +// TODO: delete again; I thought FileSystems.getDefault() does not always return the same one +/* +@TargetClass(className = "sun.nio.fs.DefaultFileSystemProvider") +final class Target_sun_nio_fs_DefaultFileSystemProvider_Web { + @Substitute + public static FileSystem theFileSystem() { + return WebImageNIOFileSystemProvider.INSTANCE.getFileSystem(null); + } +} +*/ @TargetClass(className = "sun.nio.fs.AbstractFileSystemProvider") @SuppressWarnings("all") final class Target_sun_nio_fs_AbstractFileSystemProvider_Web { diff --git a/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/system/WebImageJavaLangSubstitutions.java b/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/system/WebImageJavaLangSubstitutions.java index 56196fb4cbbb..8e4a2099f662 100644 --- a/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/system/WebImageJavaLangSubstitutions.java +++ b/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/system/WebImageJavaLangSubstitutions.java @@ -60,6 +60,8 @@ import com.oracle.svm.webimage.platform.WebImagePlatform; import com.oracle.svm.webimage.platform.WebImageWasmGCPlatform; import org.graalvm.nativeimage.ProcessProperties; +import org.graalvm.nativeimage.hosted.FieldValueTransformer; +import org.graalvm.nativeimage.impl.InternalPlatform; /* * Checkstyle: stop method name check From e99454ce5022ba3662265e48b7e5a0f2ee673987 Mon Sep 17 00:00:00 2001 From: Simon Gruenbacher Date: Tue, 5 Aug 2025 08:56:28 +0200 Subject: [PATCH 16/26] Fix module loading problem --- .../standard/Target_java_lang_Module.java | 26 ----- .../wasmgc/WasmGCUnalignedUnsafeSupport.java | 95 +++++++++++++------ 2 files changed, 65 insertions(+), 56 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_lang_Module.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_lang_Module.java index 420e278ab705..1828a6151d58 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_lang_Module.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_lang_Module.java @@ -26,9 +26,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.espresso.EspressoLanguage; -import com.oracle.truffle.espresso.libs.libjava.LibJava; 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; @@ -44,30 +42,6 @@ public final class Target_java_lang_Module { * As of JDK 15+, The native signature for these VM methods changed. These substitutions bypass * the native linking of these methods to their 'JVM_*' counterparts. */ - - /* - @Substitution(hasReceiver = true) - @TruffleBoundary - @JavaType(internalName = "Ljava/lang/Module;") static StaticObject implAddEnableNativeAccess(@JavaType(internalName = "Ljava/lang/Module;") StaticObject self) { - // no-op (TODO: add some sort of webimage context filter) - return self; - } - - */ - - @EspressoSubstitutions(type = "Ljava/lang/Module$EnableNativeAccess;") - public static final class EnableNativeAccess { - @Substitution - public static boolean isNativeAccessEnabled(@JavaType(internalName = "Ljava/lang/Module;") StaticObject target) { - return true; // TODO - } - - @Substitution - public static boolean trySetEnableNativeAccess(@JavaType(internalName = "Ljava/lang/Module;") StaticObject target) { - return true; // TODO - } - } - @Substitution @TruffleBoundary public static void addExports0(@JavaType(internalName = "Ljava/lang/Module;") StaticObject from, diff --git a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasmgc/WasmGCUnalignedUnsafeSupport.java b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasmgc/WasmGCUnalignedUnsafeSupport.java index 76238ca65a19..54b030df9e9b 100644 --- a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasmgc/WasmGCUnalignedUnsafeSupport.java +++ b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasmgc/WasmGCUnalignedUnsafeSupport.java @@ -27,6 +27,7 @@ import static jdk.graal.compiler.core.common.spi.ForeignCallDescriptor.CallSideEffect.HAS_SIDE_EFFECT; +import java.lang.reflect.Array; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -110,25 +111,33 @@ public static byte readArrayByte(Object o, long offset) { * The raw bits read from the array expanded to a long. The indexScale number of least * significant bytes contain the actual data, the rest is not relevant. */ - long longBits = switch (o) { - case boolean[] bools -> bools[index] ? 1 : 0; - case byte[] bytes -> bytes[index]; - case short[] shorts -> shorts[index]; - case char[] chars -> chars[index]; - case int[] ints -> ints[index]; - case long[] longs -> longs[index]; - case float[] floats -> Float.floatToRawIntBits(floats[index]); - case double[] doubles -> Double.doubleToRawLongBits(doubles[index]); - default -> { - if (WasmGCUnsafeSupport.includeErrorMessage()) { - WasmGCUnsafeSupport.fatalAccessError(o, "Unsupported type for unaligned array access", offset, true); + int length = Array.getLength(o); + if(inWordRemainder(o, offset)) { + return 0; + } + try { + long longBits = switch (o) { + case boolean[] bools -> bools[index] ? 1 : 0; + case byte[] bytes -> bytes[index]; + case short[] shorts -> shorts[index]; + case char[] chars -> chars[index]; + case int[] ints -> ints[index]; + case long[] longs -> longs[index]; + case float[] floats -> Float.floatToRawIntBits(floats[index]); + case double[] doubles -> Double.doubleToRawLongBits(doubles[index]); + default -> { + if (WasmGCUnsafeSupport.includeErrorMessage()) { + WasmGCUnsafeSupport.fatalAccessError(o, "Unsupported type for unaligned array access", offset, true); + } + throw new UnsupportedOperationException(); } - throw new UnsupportedOperationException(); - } - }; + }; - int rightShift = 8 * elementByteOffset(scaledOffset, indexScale); - return (byte) (longBits >> rightShift); + int rightShift = 8 * elementByteOffset(scaledOffset, indexScale); + return (byte) (longBits >> rightShift); + } catch(ArrayIndexOutOfBoundsException e) { + throw new RuntimeException("array type: " + o.getClass() + " index: " + index + " length: " + length + " offset: " + offset, e); + } } @SubstrateForeignCallTarget(stubCallingConvention = false) @@ -208,21 +217,31 @@ public static void writeArrayByte(Object o, long offset, byte value) { int index = scaledOffset / indexScale; // Byte offset within the array element value int valueOffset = elementByteOffset(scaledOffset, indexScale); - switch (o) { - case boolean[] bools -> bools[index] = value != 0; - case byte[] bytes -> bytes[index] = value; - case short[] shorts -> shorts[index] = (short) setByte(shorts[index], valueOffset, value); - case char[] chars -> chars[index] = (char) setByte(chars[index], valueOffset, value); - case int[] ints -> ints[index] = (int) setByte(ints[index], valueOffset, value); - case long[] longs -> longs[index] = setByte(longs[index], valueOffset, value); - case float[] floats -> floats[index] = Float.intBitsToFloat((int) setByte(Float.floatToRawIntBits(floats[index]), valueOffset, value)); - case double[] doubles -> doubles[index] = Double.longBitsToDouble(setByte(Double.doubleToRawLongBits(doubles[index]), valueOffset, value)); - default -> { - if (WasmGCUnsafeSupport.includeErrorMessage()) { - WasmGCUnsafeSupport.fatalAccessError(o, "Unsupported type for unaligned array access", offset, true); + + if(inWordRemainder(o, offset)) { + return; + } + try { + switch (o) { + case boolean[] bools -> bools[index] = value != 0; + case byte[] bytes -> bytes[index] = value; + case short[] shorts -> shorts[index] = (short) setByte(shorts[index], valueOffset, value); + case char[] chars -> chars[index] = (char) setByte(chars[index], valueOffset, value); + case int[] ints -> ints[index] = (int) setByte(ints[index], valueOffset, value); + case long[] longs -> longs[index] = setByte(longs[index], valueOffset, value); + case float[] floats -> + floats[index] = Float.intBitsToFloat((int) setByte(Float.floatToRawIntBits(floats[index]), valueOffset, value)); + case double[] doubles -> + doubles[index] = Double.longBitsToDouble(setByte(Double.doubleToRawLongBits(doubles[index]), valueOffset, value)); + default -> { + if (WasmGCUnsafeSupport.includeErrorMessage()) { + WasmGCUnsafeSupport.fatalAccessError(o, "Unsupported type for unaligned array access", offset, true); + } + throw new UnsupportedOperationException(); } - throw new UnsupportedOperationException(); } + } catch(ArrayIndexOutOfBoundsException e) { + throw new RuntimeException("array type: " + o.getClass() + " index: " + index + " offset: " + offset, e); } } @@ -343,4 +362,20 @@ private static int getArrayBaseOffset(JavaKind kind) { private static int getArrayIndexScale(JavaKind kind) { return ImageSingletons.lookup(ObjectLayout.class).getArrayIndexScale(kind); } + + // TODO: add test case + /** + * Detect out of bounds access that is still within the same word as the last element. + * {@link jdk.internal.misc.Unsafe#compareAndExchangeByte(java.lang.Object, long, byte, byte)} uses int accesses that are out of bounds. + * For example, there might be an int access at the end of a byte array of length 5. + * Such accesses are undefined behaviour. + * We may therefore fail gracefully instead of throwing an out-of-bounds exception. + */ + private static boolean inWordRemainder(Object o, long offset) { + int scaledOffset = getScaledOffset(o, offset); + int indexScale = getArrayIndexScale(o); + int index = scaledOffset / indexScale; + int length = Array.getLength(o); + return index >= length && scaledOffset < ((index * indexScale + 4) & ~0x03); + } } From fa891da6948f31b70e6f3f95ca183f17cd7319a1 Mon Sep 17 00:00:00 2001 From: Simon Gruenbacher Date: Tue, 5 Aug 2025 10:22:25 +0200 Subject: [PATCH 17/26] Cleanup and set os.name to 'Web Image' --- .../espresso/runtime/EspressoProperties.java | 6 +----- .../system/WebImageIOSubstitutions.java | 19 ++----------------- 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoProperties.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoProperties.java index af6748380db4..2dc610352c2e 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoProperties.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoProperties.java @@ -381,11 +381,7 @@ List defaultBootClasspath() { } path = javaHome().resolve(MODULES_IMAGE); if (Files.isReadable(path)) { - // if(Files.isDirectory(path)) { // TODO: check in into seperate PR maybe? - // bootClassPathVersion(BootClassPathType.EXPLODED); - // } else { - bootClassPathVersion(BootClassPathType.IMAGE); - // } + bootClassPathVersion(BootClassPathType.IMAGE); List paths = new ArrayList<>(1); paths.add(path); return paths; diff --git a/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/system/WebImageIOSubstitutions.java b/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/system/WebImageIOSubstitutions.java index a340401d4670..e930469cc9de 100644 --- a/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/system/WebImageIOSubstitutions.java +++ b/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/system/WebImageIOSubstitutions.java @@ -32,13 +32,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.DoubleBuffer; -import java.nio.FloatBuffer; -import java.nio.IntBuffer; -import java.nio.LongBuffer; -import java.nio.MappedByteBuffer; -import java.nio.ShortBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.LinkOption; @@ -100,6 +93,7 @@ public File toFile() { } /* +// TODO: remove after discussing where to put change @TargetClass(className = "org.graalvm.shadowed.com.google.common.jimfs.JimfsFileChannel") final class Target_org_graalvm_shadowed_com_google_common_jimfs_JimfsFileChannel { @@ -300,16 +294,7 @@ private static FileSystemProvider getDefaultProvider() { } } -// TODO: delete again; I thought FileSystems.getDefault() does not always return the same one -/* -@TargetClass(className = "sun.nio.fs.DefaultFileSystemProvider") -final class Target_sun_nio_fs_DefaultFileSystemProvider_Web { - @Substitute - public static FileSystem theFileSystem() { - return WebImageNIOFileSystemProvider.INSTANCE.getFileSystem(null); - } -} -*/ + @TargetClass(className = "sun.nio.fs.AbstractFileSystemProvider") @SuppressWarnings("all") final class Target_sun_nio_fs_AbstractFileSystemProvider_Web { From b5fa9d2e1213402c94cc4ef3696e2b6ffc83d0e2 Mon Sep 17 00:00:00 2001 From: Simon Gruenbacher Date: Fri, 22 Aug 2025 11:22:01 +0200 Subject: [PATCH 18/26] support decoding non-deflated zip files in webimage --- .../svm/hosted/webimage/WebImageFeature.java | 1 + .../system/WebImageIOSubstitutions.java | 38 ++++++++++++++++--- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/WebImageFeature.java b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/WebImageFeature.java index f6b7c85918e9..2e68e92a761e 100644 --- a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/WebImageFeature.java +++ b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/WebImageFeature.java @@ -274,6 +274,7 @@ public void afterRegistration(AfterRegistrationAccess access) { rci.initializeAtRunTime(WebImageFileSystem.class, "Static fields need to read system properties at runtime"); rci.initializeAtRunTime(FileSystemInitializer.class, "Static fields need to read system properties at runtime"); rci.initializeAtRunTime("java.nio.file.FileSystems$DefaultFileSystemHolder", "Parts of static initializer is substituted to inject custom FileSystemProvider"); + rci.initializeAtRunTime("java.util.zip.ZipFile$Source", "avoid initializing wrong file system"); for (Class jsObjectSubclass : accessImpl.findSubclasses(JSObject.class)) { rci.initializeAtRunTime(jsObjectSubclass, diff --git a/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/system/WebImageIOSubstitutions.java b/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/system/WebImageIOSubstitutions.java index e930469cc9de..552eb192f6a5 100644 --- a/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/system/WebImageIOSubstitutions.java +++ b/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/system/WebImageIOSubstitutions.java @@ -33,6 +33,7 @@ import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; +import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; @@ -42,6 +43,7 @@ import java.nio.file.spi.FileSystemProvider; import java.util.Map; import java.util.Objects; +import java.util.Set; import org.graalvm.nativeimage.ImageSingletons; @@ -295,6 +297,15 @@ private static FileSystemProvider getDefaultProvider() { } +@TargetClass(className = "sun.nio.fs.DefaultFileSystemProvider") +final class Target_sun_nio_fs_DefaultFileSystemProvider { + @Substitute + public static FileSystem theFileSystem() { + return WebImageNIOFileSystemProvider.INSTANCE.getFileSystem(null); + } +} + + @TargetClass(className = "sun.nio.fs.AbstractFileSystemProvider") @SuppressWarnings("all") final class Target_sun_nio_fs_AbstractFileSystemProvider_Web { @@ -364,20 +375,36 @@ public static Path createTempDirectory(String prefix, FileAttribute... attrs) @TargetClass(java.io.RandomAccessFile.class) @SuppressWarnings("all") final class Target_java_io_RandomAccessFile_Web { + @Alias private String path; + @Inject @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) + FileChannel fileChannel; + @Inject @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) + ByteBuffer buffer = ByteBuffer.allocate(1); @Substitute private void open0(String name, int mode) throws FileNotFoundException { - throw new UnsupportedOperationException("RandomAccessFile.open0"); + try { + Set options = Set.of(StandardOpenOption.READ, StandardOpenOption.WRITE); + fileChannel = FileChannel.open(Path.of(path), options); + } catch(IOException e) { + throw new RuntimeException(e); + } } @Substitute private int read0() throws IOException { - throw new UnsupportedOperationException("RandomAccessFile.read0"); + fileChannel.read(buffer); + return buffer.get(0); // TODO? rewind buffer? } @Substitute private int readBytes(byte[] b, int off, int len) throws IOException { - throw new UnsupportedOperationException("RandomAccessFile.readBytes"); + // TODO: map exception handling + ByteBuffer buf = ByteBuffer.allocate(len); + int read = fileChannel.read(buf); + buf.rewind(); + buf.get(b, off, Math.min(read, len)); + return read; } @Substitute @@ -397,12 +424,12 @@ public long getFilePointer() throws IOException { @Substitute private void seek0(long pos) throws IOException { - throw new UnsupportedOperationException("RandomAccessFile.seek0"); + fileChannel.position(pos); } @Substitute public long length() throws IOException { - throw new UnsupportedOperationException("RandomAccessFile.length"); + return fileChannel.size(); } @Substitute @@ -412,7 +439,6 @@ public void setLength(long newLength) throws IOException { @Substitute private static void initIDs() { - throw new UnsupportedOperationException("RandomAccessFile.initIDs"); } } From a4980b089c493c5a65a5bdce3d208891d905d107 Mon Sep 17 00:00:00 2001 From: Simon Gruenbacher Date: Mon, 8 Sep 2025 16:04:07 +0200 Subject: [PATCH 19/26] gracefully fail in image reader when file channel unsupported --- .../espresso/runtime/jimage/BasicImageReader.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/jimage/BasicImageReader.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/jimage/BasicImageReader.java index d8aadbf9ba09..bfb3a75d7778 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/jimage/BasicImageReader.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/jimage/BasicImageReader.java @@ -27,6 +27,7 @@ import java.nio.ByteOrder; import java.nio.IntBuffer; import java.nio.channels.FileChannel; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.Objects; @@ -59,7 +60,16 @@ protected BasicImageReader(Path path, ByteOrder byteOrder) throws IOException { channel = FileChannel.open(imagePath, StandardOpenOption.READ); - ByteBuffer map = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); + ByteBuffer map; + try { + map = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); + } catch(UnsupportedOperationException e) { + long read = 0; + map = ByteBuffer.allocateDirect(Math.toIntExact(channel.size())); + while(read < channel.size()) { + read += channel.read(map); + } + } int headerSize = ImageHeader.getHeaderSize(); if (map.capacity() < headerSize) { From 479742ec9e9385fdface79e92874fd99dca946fc Mon Sep 17 00:00:00 2001 From: Simon Gruenbacher Date: Mon, 8 Sep 2025 16:27:15 +0200 Subject: [PATCH 20/26] use guest file system for class loading in espresso --- .../espresso/classfile/ClasspathEntry.java | 11 +---- .../espresso/classfile/ClasspathFile.java | 4 +- .../truffle/espresso/runtime/Classpath.java | 48 ++++++++++--------- 3 files changed, 29 insertions(+), 34 deletions(-) diff --git a/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/ClasspathEntry.java b/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/ClasspathEntry.java index 49930c84adf5..e62d99ec8049 100644 --- a/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/ClasspathEntry.java +++ b/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/ClasspathEntry.java @@ -24,8 +24,6 @@ */ package com.oracle.truffle.espresso.classfile; -import java.io.File; - import com.oracle.truffle.espresso.classfile.descriptors.ByteSequence; /** @@ -37,14 +35,7 @@ public abstract class ClasspathEntry { /** * Gets the string representing the underlying path of this entry. */ - public final String path() { - return file().getPath(); - } - - /** - * Gets the File object representing the underlying path of this entry. - */ - public abstract File file(); + public abstract String path(); /** * Gets the contents of a file denoted by a given path that is relative to this classpath entry. diff --git a/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/ClasspathFile.java b/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/ClasspathFile.java index 92baf342b0d4..6a6ef7031332 100644 --- a/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/ClasspathFile.java +++ b/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/ClasspathFile.java @@ -64,8 +64,8 @@ public ClasspathFile(byte[] contents, ClasspathEntry classpathEntry, ByteSequenc @Override public String toString() { if (classpathEntry.isArchive()) { - return classpathEntry.file().getAbsolutePath() + '!' + name; + return classpathEntry.path() + '!' + name; } - return classpathEntry.file().getAbsolutePath() + File.separatorChar + name; + return classpathEntry.path() + File.separatorChar + name; } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/Classpath.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/Classpath.java index 6207dd8c18ee..838f87e1e21a 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/Classpath.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/Classpath.java @@ -30,14 +30,15 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.jar.JarFile; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; - import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.TruffleFile; import com.oracle.truffle.espresso.classfile.ClasspathEntry; import com.oracle.truffle.espresso.classfile.ClasspathFile; import com.oracle.truffle.espresso.classfile.descriptors.ByteSequence; @@ -57,10 +58,10 @@ public final class Classpath { */ @TruffleBoundary public static ClasspathEntry createEntry(String name) { - final File pathFile = new File(name); + TruffleFile pathFile = EspressoContext.get(null).getEnv().getPublicTruffleFile(name); if (pathFile.isDirectory()) { return new Directory(pathFile); - } else if (pathFile.isFile()) { + } else if (pathFile.isRegularFile()) { // regular file. EspressoContext context = EspressoContext.get(null); if (context.getJavaVersion().modulesEnabled()) { @@ -70,7 +71,9 @@ public static ClasspathEntry createEntry(String name) { } } try { - ZipFile zipFile = new JarFile(pathFile, false, OPEN_READ, context.getJavaVersion().toRunTimeVersion()); + Path file = Files.createTempFile(null, null); + Files.write(file, pathFile.readAllBytes()); + ZipFile zipFile = new JarFile(file.toFile(), false, OPEN_READ, context.getJavaVersion().toRunTimeVersion()); return new Archive(pathFile, zipFile); } catch (IOException ignored) { } @@ -85,9 +88,9 @@ public static ClasspathEntry createEntry(String name) { * archive file. */ static final class PlainFile extends ClasspathEntry { - private final File file; + private final TruffleFile file; - PlainFile(File file) { + PlainFile(TruffleFile file) { this.file = file; } @@ -97,8 +100,8 @@ public ClasspathFile readFile(ByteSequence archiveName) { } @Override - public File file() { - return file; + public String path() { + return file.getPath(); } } @@ -108,9 +111,9 @@ public File file() { public static final class Directory extends ClasspathEntry { private static final boolean REPLACE_SEPARATOR = File.separatorChar != '/'; - private final File directory; + private final TruffleFile directory; - public Directory(File directory) { + public Directory(TruffleFile directory) { // makes getParent work as expected with relative pathnames this.directory = directory.getAbsoluteFile(); } @@ -121,10 +124,11 @@ public ClasspathFile readFile(ByteSequence archiveName) { if (REPLACE_SEPARATOR) { fsPath = fsPath.replace('/', File.separatorChar); } - final File file = new File(directory, fsPath); + TruffleFile file = directory.resolve(fsPath); if (file.exists()) { try { - return new ClasspathFile(Files.readAllBytes(file.toPath()), this, archiveName); + byte[] bytes = file.readAllBytes(); + return new ClasspathFile(bytes, this, archiveName); } catch (IOException ioException) { return null; } @@ -133,8 +137,8 @@ public ClasspathFile readFile(ByteSequence archiveName) { } @Override - public File file() { - return directory; + public String path() { + return directory.getPath(); } @Override @@ -147,10 +151,10 @@ public boolean isDirectory() { * Represents a classpath entry that is a path to an existing zip/jar archive file. */ static final class Archive extends ClasspathEntry { - private final File file; + private final TruffleFile file; private final ZipFile zipFile; - Archive(File file, ZipFile zipFile) { + Archive(TruffleFile file, ZipFile zipFile) { this.file = file; this.zipFile = zipFile; } @@ -168,8 +172,8 @@ public ClasspathFile readFile(ByteSequence archiveName) { } @Override - public File file() { - return file; + public String path() { + return file.getPath(); } @Override @@ -183,19 +187,19 @@ public boolean isArchive() { * Represents a classpath entry that is a path to a jimage modules file. */ static final class Modules extends ClasspathEntry { - private final File file; + private final TruffleFile file; private final JImageHelper helper; private final JImageExtensions extensions; - Modules(File file, JImageHelper helper, JImageExtensions extensions) { + Modules(TruffleFile file, JImageHelper helper, JImageExtensions extensions) { this.file = file; this.helper = helper; this.extensions = extensions; } @Override - public File file() { - return file; + public String path() { + return file.getPath(); } @Override From c3f074f82a745f17ab1db5144c9be051b785ad80 Mon Sep 17 00:00:00 2001 From: Simon Gruenbacher Date: Thu, 11 Sep 2025 16:34:30 +0200 Subject: [PATCH 21/26] remove redundant security manager version fix --- .../libs/libjava/impl/Target_java_lang_SecurityManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_SecurityManager.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_SecurityManager.java index 730bd7904cae..f637f17781f9 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_SecurityManager.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_SecurityManager.java @@ -43,7 +43,7 @@ public final class Target_java_lang_SecurityManager { @SuppressWarnings("deprecated") @Substitution(hasReceiver = true, languageFilter = VersionFilter.Java21.class) public static @JavaType(Class[].class) StaticObject getClassContext(@JavaType(SecurityManager.class) StaticObject self, @Inject VM vm, @Inject LibsMeta libsMeta, @Inject EspressoContext context) { - if (libsMeta.java_lang_SecurityManager_initialized != null && !libsMeta.java_lang_SecurityManager_initialized.getBoolean(self)) { // TODO: How to deal with java version stuff? + if (!libsMeta.java_lang_SecurityManager_initialized.getBoolean(self)) { throw Throw.throwSecurityException("security manager not initialized", context); } return vm.JVM_GetClassContext(); From d9c3fb8539326c33166908f874f4c4af5c4932ec Mon Sep 17 00:00:00 2001 From: Simon Gruenbacher Date: Thu, 11 Sep 2025 16:37:04 +0200 Subject: [PATCH 22/26] remove hotfix for GR-48016 --- .../src/com/oracle/truffle/api/InternalResource.java | 1 - 1 file changed, 1 deletion(-) diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/InternalResource.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/InternalResource.java index c8f9b0a31233..e21c0052e354 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/InternalResource.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/InternalResource.java @@ -279,7 +279,6 @@ public void unpackResourceFiles(Path source, Path target, Path relativizeTo, Pre Properties fileList = loadFileList(source); for (var e : fileList.entrySet()) { Path resource = Path.of((String) e.getKey()); - if(resource.getFileName().endsWith(".DS_Store")) continue; // resolves 'could not find metadata file' problem on mac; DO NOT CHECK IN Set attrs = parseAttrs((String) e.getValue()); if (resource.isAbsolute()) { throw new IllegalArgumentException("The file list must contain only relative paths, but the absolute path " + resource + " was given."); From c7ffa029c38f02f6d113d26763a2c7676e435386 Mon Sep 17 00:00:00 2001 From: Simon Gruenbacher Date: Thu, 11 Sep 2025 16:55:21 +0200 Subject: [PATCH 23/26] set webimage substitutions to match status after merged PR GR-69033 --- .../system/WebImageIOSubstitutions.java | 79 ++++++++----------- .../system/WebImageJavaLangSubstitutions.java | 2 - 2 files changed, 34 insertions(+), 47 deletions(-) diff --git a/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/system/WebImageIOSubstitutions.java b/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/system/WebImageIOSubstitutions.java index 552eb192f6a5..e3b7d7e3d8fe 100644 --- a/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/system/WebImageIOSubstitutions.java +++ b/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/system/WebImageIOSubstitutions.java @@ -94,25 +94,6 @@ public File toFile() { } } -/* -// TODO: remove after discussing where to put change -@TargetClass(className = "org.graalvm.shadowed.com.google.common.jimfs.JimfsFileChannel") -final class Target_org_graalvm_shadowed_com_google_common_jimfs_JimfsFileChannel { - - - // Note: This needs to be a subclass IOException so that BasicImageReader in Espresso fails gracefully. - // In jimfs this would be an UnsupportedOperationException, which is not caught in Espresso. - // The operation is not supported because MappedByteBuffer is an abstract class inheriting from the sealed class Buffer, - // and thus cannot be implemented. - - - @Substitute - public MappedByteBuffer map(FileChannel.MapMode mode, long position, long size) throws IOException { - throw new IOException("cannot map jimfs file"); - } -} -*/ - @TargetClass(java.io.FileInputStream.class) final class Target_java_io_FileInputStream_Web { @@ -297,15 +278,6 @@ private static FileSystemProvider getDefaultProvider() { } -@TargetClass(className = "sun.nio.fs.DefaultFileSystemProvider") -final class Target_sun_nio_fs_DefaultFileSystemProvider { - @Substitute - public static FileSystem theFileSystem() { - return WebImageNIOFileSystemProvider.INSTANCE.getFileSystem(null); - } -} - - @TargetClass(className = "sun.nio.fs.AbstractFileSystemProvider") @SuppressWarnings("all") final class Target_sun_nio_fs_AbstractFileSystemProvider_Web { @@ -375,34 +347,42 @@ public static Path createTempDirectory(String prefix, FileAttribute... attrs) @TargetClass(java.io.RandomAccessFile.class) @SuppressWarnings("all") final class Target_java_io_RandomAccessFile_Web { - @Alias private String path; - @Inject @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) - FileChannel fileChannel; - @Inject @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) - ByteBuffer buffer = ByteBuffer.allocate(1); + @Alias static int O_RDWR; + @Alias FileChannel channel; @Substitute private void open0(String name, int mode) throws FileNotFoundException { + Path path = Path.of(name); + if (Files.notExists(path)) { + throw new FileNotFoundException(name + " does not exist."); + } + if (Files.isDirectory(path)) { + throw new FileNotFoundException(name + " is a directory."); + } + if ((mode & O_RDWR) != 0) { + throw new UnsupportedOperationException("open RandomAccessFile with mode other than readonly."); + } + try { - Set options = Set.of(StandardOpenOption.READ, StandardOpenOption.WRITE); - fileChannel = FileChannel.open(Path.of(path), options); - } catch(IOException e) { + Set options = Set.of(StandardOpenOption.READ); + channel = FileChannel.open(Path.of(name), options); + } catch (IOException e) { throw new RuntimeException(e); } } @Substitute private int read0() throws IOException { - fileChannel.read(buffer); - return buffer.get(0); // TODO? rewind buffer? + ByteBuffer buffer = ByteBuffer.allocate(1); + int result = channel.read(buffer); + return buffer.get(0); } @Substitute - private int readBytes(byte[] b, int off, int len) throws IOException { - // TODO: map exception handling + private int readBytes0(byte[] b, int off, int len) throws IOException { ByteBuffer buf = ByteBuffer.allocate(len); - int read = fileChannel.read(buf); - buf.rewind(); + int read = channel.read(buf); + buf.flip(); buf.get(b, off, Math.min(read, len)); return read; } @@ -419,17 +399,17 @@ private void writeBytes(byte[] b, int off, int len) throws IOException { @Substitute public long getFilePointer() throws IOException { - throw new UnsupportedOperationException("RandomAccessFile.getFilePointer"); + return channel.position(); } @Substitute private void seek0(long pos) throws IOException { - fileChannel.position(pos); + channel.position(pos); } @Substitute public long length() throws IOException { - return fileChannel.size(); + return channel.size(); } @Substitute @@ -439,6 +419,7 @@ public void setLength(long newLength) throws IOException { @Substitute private static void initIDs() { + // do nothing } } @@ -467,6 +448,14 @@ public long size() { } +@TargetClass(className = "sun.nio.fs.DefaultFileSystemProvider") +final class Target_sun_nio_fs_DefaultFileSystemProvider { + @Substitute + public static FileSystem theFileSystem() { + return WebImageNIOFileSystemProvider.INSTANCE.getFileSystem(null); + } +} + @TargetClass(className = "java.nio.file.TempFileHelper") final class Target_java_nio_file_TempFileHelper_Web { diff --git a/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/system/WebImageJavaLangSubstitutions.java b/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/system/WebImageJavaLangSubstitutions.java index 8e4a2099f662..56196fb4cbbb 100644 --- a/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/system/WebImageJavaLangSubstitutions.java +++ b/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/substitute/system/WebImageJavaLangSubstitutions.java @@ -60,8 +60,6 @@ import com.oracle.svm.webimage.platform.WebImagePlatform; import com.oracle.svm.webimage.platform.WebImageWasmGCPlatform; import org.graalvm.nativeimage.ProcessProperties; -import org.graalvm.nativeimage.hosted.FieldValueTransformer; -import org.graalvm.nativeimage.impl.InternalPlatform; /* * Checkstyle: stop method name check From 7b1945068ea56c1b6bf6565f11c5686625106897 Mon Sep 17 00:00:00 2001 From: Simon Gruenbacher Date: Fri, 12 Sep 2025 08:33:28 +0200 Subject: [PATCH 24/26] (1) remove stdin index to be added in no-native branch (2) integrate FileChannel fallback PR --- .../libs/libjava/impl/Target_java_lang_System.java | 11 ----------- .../espresso/runtime/jimage/BasicImageReader.java | 13 ++++++------- .../standard/Target_java_lang_Module.java | 1 + 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_System.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_System.java index 095403d94997..913161bccb01 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_System.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/libjava/impl/Target_java_lang_System.java @@ -96,9 +96,6 @@ public static final class Raw { } known[props.stdoutEncodingNdx] = java.lang.System.getProperty("stdout.encoding"); known[props.stderrEncodingNdx] = java.lang.System.getProperty("stderr.encoding"); - if(ctx.getJavaVersion().java25OrLater()) { - known[props.stdinEncodingNdx] = java.lang.System.getProperty("stdin.encoding"); - } known[props.osNameNdx] = java.lang.System.getProperty("os.name"); known[props.osArchNdx] = java.lang.System.getProperty("os.arch"); @@ -179,7 +176,6 @@ private static final class Props { private final int socksProxyHostNdx; private final int socksProxyPortNdx; private final int stderrEncodingNdx; - private final int stdinEncodingNdx; private final int stdoutEncodingNdx; private final int sunArchAbiNdx; private final int sunArchDataModelNdx; @@ -232,13 +228,6 @@ private Props(EspressoContext ctx) { socksProxyHostNdx = guestRaw.lookupDeclaredField(ctx.getNames().getOrCreate("_socksProxyHost_NDX"), Types._int).getInt(guestRaw.tryInitializeAndGetStatics()); socksProxyPortNdx = guestRaw.lookupDeclaredField(ctx.getNames().getOrCreate("_socksProxyPort_NDX"), Types._int).getInt(guestRaw.tryInitializeAndGetStatics()); stderrEncodingNdx = guestRaw.lookupDeclaredField(ctx.getNames().getOrCreate("_stderr_encoding_NDX"), Types._int).getInt(guestRaw.tryInitializeAndGetStatics()); - - if(ctx.getJavaVersion().java25OrLater()) { // https://github.com/openjdk/valhalla/commit/15f42e348d4068bd90dd75b270a372353fe0ed07#diff-0b761d753142789aae9ff9bb970787ee6ece0df8f85852df461edc5b2e13f875 - stdinEncodingNdx = guestRaw.lookupDeclaredField(ctx.getNames().getOrCreate("_stdin_encoding_NDX"), Types._int).getInt(guestRaw.tryInitializeAndGetStatics()); - } else { - stdinEncodingNdx = -1; - } - stdoutEncodingNdx = guestRaw.lookupDeclaredField(ctx.getNames().getOrCreate("_stdout_encoding_NDX"), Types._int).getInt(guestRaw.tryInitializeAndGetStatics()); sunArchAbiNdx = guestRaw.lookupDeclaredField(ctx.getNames().getOrCreate("_sun_arch_abi_NDX"), Types._int).getInt(guestRaw.tryInitializeAndGetStatics()); sunArchDataModelNdx = guestRaw.lookupDeclaredField(ctx.getNames().getOrCreate("_sun_arch_data_model_NDX"), Types._int).getInt(guestRaw.tryInitializeAndGetStatics()); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/jimage/BasicImageReader.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/jimage/BasicImageReader.java index bfb3a75d7778..515598067324 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/jimage/BasicImageReader.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/jimage/BasicImageReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, 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 @@ -27,7 +27,6 @@ import java.nio.ByteOrder; import java.nio.IntBuffer; import java.nio.channels.FileChannel; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.Objects; @@ -63,12 +62,12 @@ protected BasicImageReader(Path path, ByteOrder byteOrder) throws IOException { ByteBuffer map; try { map = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); - } catch(UnsupportedOperationException e) { - long read = 0; + } catch (UnsupportedOperationException e) { + long lastRead; map = ByteBuffer.allocateDirect(Math.toIntExact(channel.size())); - while(read < channel.size()) { - read += channel.read(map); - } + do { + lastRead = channel.read(map); + } while (lastRead >= 0 && map.hasRemaining()); } int headerSize = ImageHeader.getHeaderSize(); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_lang_Module.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_lang_Module.java index 1828a6151d58..67d799b72d1b 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_lang_Module.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_lang_Module.java @@ -42,6 +42,7 @@ public final class Target_java_lang_Module { * As of JDK 15+, The native signature for these VM methods changed. These substitutions bypass * the native linking of these methods to their 'JVM_*' counterparts. */ + @Substitution @TruffleBoundary public static void addExports0(@JavaType(internalName = "Ljava/lang/Module;") StaticObject from, From 1ddf2aadbb98a331bf5e172c1a8da0c8e1f42aca Mon Sep 17 00:00:00 2001 From: Simon Gruenbacher Date: Tue, 23 Sep 2025 12:50:08 +0200 Subject: [PATCH 25/26] apply suggested changes to unaligned unsafe support --- .../wasmgc/WasmGCUnalignedUnsafeSupport.java | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasmgc/WasmGCUnalignedUnsafeSupport.java b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasmgc/WasmGCUnalignedUnsafeSupport.java index 54b030df9e9b..7931ea6ca951 100644 --- a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasmgc/WasmGCUnalignedUnsafeSupport.java +++ b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasmgc/WasmGCUnalignedUnsafeSupport.java @@ -107,15 +107,14 @@ public static byte readArrayByte(Object o, long offset) { int scaledOffset = getScaledOffset(o, offset); int indexScale = getArrayIndexScale(o); int index = scaledOffset / indexScale; - /* - * The raw bits read from the array expanded to a long. The indexScale number of least - * significant bytes contain the actual data, the rest is not relevant. - */ - int length = Array.getLength(o); - if(inWordRemainder(o, offset)) { + if (inWordRemainder(o, offset)) { return 0; } try { + /* + * The raw bits read from the array expanded to a long. The indexScale number of least + * significant bytes contain the actual data, the rest is not relevant. + */ long longBits = switch (o) { case boolean[] bools -> bools[index] ? 1 : 0; case byte[] bytes -> bytes[index]; @@ -135,8 +134,12 @@ public static byte readArrayByte(Object o, long offset) { int rightShift = 8 * elementByteOffset(scaledOffset, indexScale); return (byte) (longBits >> rightShift); - } catch(ArrayIndexOutOfBoundsException e) { - throw new RuntimeException("array type: " + o.getClass() + " index: " + index + " length: " + length + " offset: " + offset, e); + } catch (ArrayIndexOutOfBoundsException e) { + if (WasmGCUnsafeSupport.includeErrorMessage()) { + WasmGCUnsafeSupport.fatalAccessError(o, e.toString(), offset, true); + } + + throw e; } } @@ -218,7 +221,7 @@ public static void writeArrayByte(Object o, long offset, byte value) { // Byte offset within the array element value int valueOffset = elementByteOffset(scaledOffset, indexScale); - if(inWordRemainder(o, offset)) { + if (inWordRemainder(o, offset)) { return; } try { @@ -230,18 +233,22 @@ public static void writeArrayByte(Object o, long offset, byte value) { case int[] ints -> ints[index] = (int) setByte(ints[index], valueOffset, value); case long[] longs -> longs[index] = setByte(longs[index], valueOffset, value); case float[] floats -> - floats[index] = Float.intBitsToFloat((int) setByte(Float.floatToRawIntBits(floats[index]), valueOffset, value)); + floats[index] = Float.intBitsToFloat((int) setByte(Float.floatToRawIntBits(floats[index]), valueOffset, value)); case double[] doubles -> - doubles[index] = Double.longBitsToDouble(setByte(Double.doubleToRawLongBits(doubles[index]), valueOffset, value)); + doubles[index] = Double.longBitsToDouble(setByte(Double.doubleToRawLongBits(doubles[index]), valueOffset, value)); default -> { if (WasmGCUnsafeSupport.includeErrorMessage()) { - WasmGCUnsafeSupport.fatalAccessError(o, "Unsupported type for unaligned array access", offset, true); + WasmGCUnsafeSupport.fatalAccessError(o, "Unsupported type for unaligned array access", offset, false); } throw new UnsupportedOperationException(); } } - } catch(ArrayIndexOutOfBoundsException e) { - throw new RuntimeException("array type: " + o.getClass() + " index: " + index + " offset: " + offset, e); + } catch (ArrayIndexOutOfBoundsException e) { + if (WasmGCUnsafeSupport.includeErrorMessage()) { + WasmGCUnsafeSupport.fatalAccessError(o, e.toString(), offset, false); + } + + throw e; } } @@ -363,13 +370,12 @@ private static int getArrayIndexScale(JavaKind kind) { return ImageSingletons.lookup(ObjectLayout.class).getArrayIndexScale(kind); } - // TODO: add test case /** - * Detect out of bounds access that is still within the same word as the last element. - * {@link jdk.internal.misc.Unsafe#compareAndExchangeByte(java.lang.Object, long, byte, byte)} uses int accesses that are out of bounds. - * For example, there might be an int access at the end of a byte array of length 5. - * Such accesses are undefined behaviour. - * We may therefore fail gracefully instead of throwing an out-of-bounds exception. + * Detect out of bounds access that is still within the same 4-byte word as the last element. + * {@link jdk.internal.misc.Unsafe#compareAndExchangeByte(java.lang.Object, long, byte, byte)} + * uses int accesses that are out of bounds. For example, there might be an int access at the + * end of a byte array of length 5. Such accesses are undefined behaviour. We may therefore + * ignore such accesses instead of throwing an out-of-bounds exception. */ private static boolean inWordRemainder(Object o, long offset) { int scaledOffset = getScaledOffset(o, offset); From 9ae13dd1eba87b864b62ee13ccd194e7ff09d494 Mon Sep 17 00:00:00 2001 From: Simon Gruenbacher Date: Mon, 29 Sep 2025 08:53:38 +0200 Subject: [PATCH 26/26] revert TruffleFile changes --- .../espresso/classfile/ClasspathEntry.java | 11 ++++- .../espresso/classfile/ClasspathFile.java | 4 +- .../truffle/espresso/runtime/Classpath.java | 48 +++++++++---------- 3 files changed, 34 insertions(+), 29 deletions(-) diff --git a/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/ClasspathEntry.java b/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/ClasspathEntry.java index e62d99ec8049..49930c84adf5 100644 --- a/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/ClasspathEntry.java +++ b/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/ClasspathEntry.java @@ -24,6 +24,8 @@ */ package com.oracle.truffle.espresso.classfile; +import java.io.File; + import com.oracle.truffle.espresso.classfile.descriptors.ByteSequence; /** @@ -35,7 +37,14 @@ public abstract class ClasspathEntry { /** * Gets the string representing the underlying path of this entry. */ - public abstract String path(); + public final String path() { + return file().getPath(); + } + + /** + * Gets the File object representing the underlying path of this entry. + */ + public abstract File file(); /** * Gets the contents of a file denoted by a given path that is relative to this classpath entry. diff --git a/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/ClasspathFile.java b/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/ClasspathFile.java index 6a6ef7031332..92baf342b0d4 100644 --- a/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/ClasspathFile.java +++ b/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/ClasspathFile.java @@ -64,8 +64,8 @@ public ClasspathFile(byte[] contents, ClasspathEntry classpathEntry, ByteSequenc @Override public String toString() { if (classpathEntry.isArchive()) { - return classpathEntry.path() + '!' + name; + return classpathEntry.file().getAbsolutePath() + '!' + name; } - return classpathEntry.path() + File.separatorChar + name; + return classpathEntry.file().getAbsolutePath() + File.separatorChar + name; } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/Classpath.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/Classpath.java index 838f87e1e21a..6207dd8c18ee 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/Classpath.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/Classpath.java @@ -30,15 +30,14 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.jar.JarFile; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; + import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.TruffleFile; import com.oracle.truffle.espresso.classfile.ClasspathEntry; import com.oracle.truffle.espresso.classfile.ClasspathFile; import com.oracle.truffle.espresso.classfile.descriptors.ByteSequence; @@ -58,10 +57,10 @@ public final class Classpath { */ @TruffleBoundary public static ClasspathEntry createEntry(String name) { - TruffleFile pathFile = EspressoContext.get(null).getEnv().getPublicTruffleFile(name); + final File pathFile = new File(name); if (pathFile.isDirectory()) { return new Directory(pathFile); - } else if (pathFile.isRegularFile()) { + } else if (pathFile.isFile()) { // regular file. EspressoContext context = EspressoContext.get(null); if (context.getJavaVersion().modulesEnabled()) { @@ -71,9 +70,7 @@ public static ClasspathEntry createEntry(String name) { } } try { - Path file = Files.createTempFile(null, null); - Files.write(file, pathFile.readAllBytes()); - ZipFile zipFile = new JarFile(file.toFile(), false, OPEN_READ, context.getJavaVersion().toRunTimeVersion()); + ZipFile zipFile = new JarFile(pathFile, false, OPEN_READ, context.getJavaVersion().toRunTimeVersion()); return new Archive(pathFile, zipFile); } catch (IOException ignored) { } @@ -88,9 +85,9 @@ public static ClasspathEntry createEntry(String name) { * archive file. */ static final class PlainFile extends ClasspathEntry { - private final TruffleFile file; + private final File file; - PlainFile(TruffleFile file) { + PlainFile(File file) { this.file = file; } @@ -100,8 +97,8 @@ public ClasspathFile readFile(ByteSequence archiveName) { } @Override - public String path() { - return file.getPath(); + public File file() { + return file; } } @@ -111,9 +108,9 @@ public String path() { public static final class Directory extends ClasspathEntry { private static final boolean REPLACE_SEPARATOR = File.separatorChar != '/'; - private final TruffleFile directory; + private final File directory; - public Directory(TruffleFile directory) { + public Directory(File directory) { // makes getParent work as expected with relative pathnames this.directory = directory.getAbsoluteFile(); } @@ -124,11 +121,10 @@ public ClasspathFile readFile(ByteSequence archiveName) { if (REPLACE_SEPARATOR) { fsPath = fsPath.replace('/', File.separatorChar); } - TruffleFile file = directory.resolve(fsPath); + final File file = new File(directory, fsPath); if (file.exists()) { try { - byte[] bytes = file.readAllBytes(); - return new ClasspathFile(bytes, this, archiveName); + return new ClasspathFile(Files.readAllBytes(file.toPath()), this, archiveName); } catch (IOException ioException) { return null; } @@ -137,8 +133,8 @@ public ClasspathFile readFile(ByteSequence archiveName) { } @Override - public String path() { - return directory.getPath(); + public File file() { + return directory; } @Override @@ -151,10 +147,10 @@ public boolean isDirectory() { * Represents a classpath entry that is a path to an existing zip/jar archive file. */ static final class Archive extends ClasspathEntry { - private final TruffleFile file; + private final File file; private final ZipFile zipFile; - Archive(TruffleFile file, ZipFile zipFile) { + Archive(File file, ZipFile zipFile) { this.file = file; this.zipFile = zipFile; } @@ -172,8 +168,8 @@ public ClasspathFile readFile(ByteSequence archiveName) { } @Override - public String path() { - return file.getPath(); + public File file() { + return file; } @Override @@ -187,19 +183,19 @@ public boolean isArchive() { * Represents a classpath entry that is a path to a jimage modules file. */ static final class Modules extends ClasspathEntry { - private final TruffleFile file; + private final File file; private final JImageHelper helper; private final JImageExtensions extensions; - Modules(TruffleFile file, JImageHelper helper, JImageExtensions extensions) { + Modules(File file, JImageHelper helper, JImageExtensions extensions) { this.file = file; this.helper = helper; this.extensions = extensions; } @Override - public String path() { - return file.getPath(); + public File file() { + return file; } @Override