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 81ce67168a52..a6ac976af285 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.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.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/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.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 15c20c810abf..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 @@ -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 { @@ -73,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 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/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/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 36aa9f507c06..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 @@ -34,11 +34,11 @@ * 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 { - + // to avoid code duplication of NativeDispatcher operations + private final TruffleDispatcher truffleDispatcher = new TruffleDispatcher(); static { sun.nio.ch.IOUtil.load(); } @@ -106,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 @@ -213,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/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.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/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/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/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 a1c5c7fd82d2..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 @@ -32,8 +32,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 TruffleBasicFileAttributeView implements BasicFileAttributeView { 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..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 @@ -31,8 +31,7 @@ * 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 { 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..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 @@ -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"); @@ -184,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 1ce296576c97..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 @@ -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; @@ -51,10 +50,10 @@ 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 { + public static final int DEFAULT_FILE_PERMISSIONS_MASK = 511; static { // ensure 'nio' is loaded. Also loads 'net' as a side-effect. @@ -116,7 +115,7 @@ public FileSystem getFileSystem(URI uri) { @Override public Path getPath(URI uri) { - throw new UnsupportedOperationException(); + return new TrufflePath(theFileSystem(), uri.getPath()); } @Override @@ -126,19 +125,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 @@ -156,7 +185,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, @@ -170,7 +199,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)); @@ -235,11 +264,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)); @@ -321,10 +350,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 @@ -342,9 +369,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; @@ -356,7 +383,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/TruffleFilteredDirectoryStream.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFilteredDirectoryStream.java index ebb46d071693..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,18 +31,19 @@ import java.util.Objects; /** - * This file must be compatible with all Java versions supported by Espresso, strict Java 8 - * compatibility is required. + * 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 @@ -54,7 +55,7 @@ public boolean hasNext() { } @Override - public Object next() { + public String next() { return next0(this); } } @@ -62,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 @@ -74,7 +75,7 @@ public void close() throws IOException { } @Override - public Iterator iterator() { + public Iterator iterator() { return iterator0(this, ForeignIterator.class); } } @@ -86,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); @@ -95,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); @@ -117,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.io/src/sun/nio/fs/TrufflePath.java b/espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TrufflePath.java index 6db963b8119e..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 @@ -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 { @@ -265,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.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.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 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; @CompilationFinal private int maxStackTraceDepth; // endregion Options @@ -261,6 +267,12 @@ 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()); } @@ -321,6 +333,33 @@ private void extractDataFrom(EspressoLanguage other) { languageCache.importFrom(other.getLanguageCache()); } + private static String setNativeBackendId(final TruffleLanguage.Env env) { + boolean isAllowed = env.isNativeAccessAllowed(); + // if the Env allows, this might be overwritten. + String nativeBackend = NoNativeAccess.Provider.ID; + if (env.getOptions().hasBeenSet(EspressoOptions.NativeBackend)) { + String userNativeBackend = env.getOptions().get(EspressoOptions.NativeBackend); + if (!isAllowed && !userNativeBackend.equals(nativeBackend)) { + throw EspressoError.fatal("trying to set NativeBackend even though NativeAccess is disabled"); + } + return userNativeBackend; + + } else if (isAllowed) { + // 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 +393,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 +640,14 @@ public boolean useEspressoLibs() { return useEspressoLibs; } + 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 c6e59f2f6cfc..e5fe8f9ff599 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;"); @@ -128,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;"); @@ -140,8 +144,8 @@ 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;"); public static final Symbol jdk_internal_loader_ClassLoaders = SYMBOLS.putType("Ljdk/internal/loader/ClassLoaders;"); @@ -159,10 +163,34 @@ 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;"); + // 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;"); + // 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;"); @@ -756,15 +784,54 @@ 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"); 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.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.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.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"); @@ -1445,6 +1512,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 118c8a094124..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 @@ -23,7 +23,9 @@ package com.oracle.truffle.espresso.ffi; 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; @@ -31,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; @@ -48,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); @@ -61,20 +66,24 @@ 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 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()); } // 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); } @@ -114,8 +123,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; } @@ -140,6 +152,9 @@ public boolean hasFallbackSymbols() { @Override public boolean isFallbackSymbol(TruffleObject symbol) { + if (symbol instanceof SubstitutionFactoryWrapper) { + return false; + } return delegate.isFallbackSymbol(symbol); } @@ -158,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); @@ -183,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 b3d7e3104fe7..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 @@ -38,6 +38,7 @@ import com.oracle.truffle.api.interop.UnsupportedTypeException; import com.oracle.truffle.espresso.EspressoOptions; import com.oracle.truffle.espresso.classfile.JavaKind; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; import com.oracle.truffle.espresso.meta.EspressoError; import com.oracle.truffle.espresso.runtime.EspressoProperties; @@ -219,23 +220,47 @@ default boolean isBuiltIn(@SuppressWarnings("unused") String libname) { * {@link InteropLibrary#hasBufferElements(Object) buffer}. * @throws IllegalArgumentException if the size is negative */ - @Buffer - TruffleObject allocateMemory(long size); + default @Buffer TruffleObject allocateMemory(long size) { + long address = nativeMemory().allocateMemory(size); + if (address == 0) { + return null; + } + return RawPointer.create(address); + } /** * Similar to realloc. The result of allocating a 0-sized buffer is an implementation detail. - * + * * @return null if the memory cannot be re-allocated. Otherwise, a * {@link InteropLibrary#hasBufferElements(Object) buffer}. * @throws IllegalArgumentException if the size is negative */ - @Buffer - TruffleObject reallocateMemory(@Pointer TruffleObject buffer, long newSize); + default @Buffer TruffleObject reallocateMemory(@Pointer TruffleObject buffer, long newSize) { + long address = 0; + try { + address = InteropLibrary.getUncached().asPointer(buffer); + } catch (UnsupportedMessageException e) { + throw EspressoError.shouldNotReachHere(e); + } + long rawPointer = nativeMemory().reallocateMemory(address, newSize); + if (rawPointer == 0) { + return null; + } + return RawPointer.create(rawPointer); + } /** * Similar to free. Accessing the buffer after free may cause explosive undefined behavior. */ - void freeMemory(@Pointer TruffleObject buffer); + default void freeMemory(@Pointer TruffleObject buffer) { + long address = 0; + try { + address = InteropLibrary.getUncached().asPointer(buffer); + } catch (UnsupportedMessageException e) { + 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/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 new file mode 100644 index 000000000000..e90d86a9a33a --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ByteArrayChunkedMemoryImpl.java @@ -0,0 +1,194 @@ +/* + * 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; + +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), 0); + 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..1378a561a530 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ChunkedNativeMemory.java @@ -0,0 +1,115 @@ +/* + * 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; +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..285fb4a5b5c1 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/NativeMemory.java @@ -0,0 +1,162 @@ +/* + * 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 { + 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; + } + + 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 new file mode 100644 index 000000000000..f9e0fd18d416 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ProtoChunkedMemoryImpl.java @@ -0,0 +1,305 @@ +/* + * 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; + +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 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); + // 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); + } + + @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 + 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); + + 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); + } + + @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 new file mode 100644 index 000000000000..7074cc9a7e5c --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/UnsafeNativeMemory.java @@ -0,0 +1,190 @@ +/* + * 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 { + + 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..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 @@ -40,12 +40,13 @@ 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.UnsafeNativeMemory; import com.oracle.truffle.espresso.impl.EmptyKeysArray; import com.oracle.truffle.espresso.meta.EspressoError; import com.oracle.truffle.espresso.substitutions.Collect; @@ -78,7 +79,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 +94,57 @@ 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..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 @@ -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; @@ -59,6 +60,8 @@ import com.oracle.truffle.espresso.ffi.RawPointer; import com.oracle.truffle.espresso.ffi.SignatureCallNode; import com.oracle.truffle.espresso.ffi.TruffleByteBuffer; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; +import com.oracle.truffle.espresso.ffi.memory.UnsafeNativeMemory; import com.oracle.truffle.espresso.meta.EspressoError; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; import com.oracle.truffle.espresso.substitutions.Collect; @@ -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/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/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..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 @@ -23,8 +23,18 @@ 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; +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; @@ -48,15 +58,97 @@ 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); } + 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) { - 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); + } + + 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 == 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); } - throw throwIOException(getMessageBoundary(e), context); + 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); + } + + private static boolean isConnectionResetException(IOException e) { + return e.getClass() == SocketException.class && (getMessageBoundary(e).equals("Connection reset")); } public static EspressoException throwNonReadable(EspressoContext context) { @@ -85,43 +177,53 @@ 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, 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(); - throw meta.throwExceptionWithMessage(meta.java_nio_file_NotLinkException, file); + 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 d9d09c5de7f7..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; @@ -30,17 +31,38 @@ 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.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; +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; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; @@ -53,18 +75,22 @@ 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; +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; 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; /** @@ -85,7 +111,14 @@ 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; @@ -113,6 +146,12 @@ 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 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 @@ -183,6 +222,398 @@ 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; + } + + /** + * 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); + } + + /** + * 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) { + 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) { + 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); + } + } + + /** + * 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. * @@ -198,8 +629,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; } @@ -227,14 +658,13 @@ 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 public long length(@JavaType(Object.class) StaticObject self, FDAccess fdAccess) { - StaticObject fileDesc = getFileDesc(self, fdAccess); - return length(getFD(fileDesc)); + return length(getFD(self, fdAccess)); } /** @@ -242,11 +672,56 @@ 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 sizeImpl(getSeekableChannel(fd), 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 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) { + try { + return getWritableChannel(self, fdAccess).write(bytes); + } catch (ClosedByInterruptException e) { + return ioStatusSync.UNAVAILABLE; + } catch (NonWritableChannelException e) { + throw Throw.throwNonWritable(context); + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } + } + + /** + * 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); } - return 0; // unknown } /** @@ -254,9 +729,7 @@ 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 ByteBuffer containing the bytes to write. * @return The number of bytes written, possibly zero. * @see java.io.FileOutputStream#write(byte[], int, int) */ @@ -265,8 +738,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); } /** @@ -278,12 +750,69 @@ 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); + } + + /** + * 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 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 writev(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess, + 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 sequentialWritev(self, fdAccess, address, length); + } + + /** + * 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 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 readv(@JavaType(Object.class) StaticObject self, + FDAccess fdAccess, + 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 sequentialReadv(self, fdAccess, address, length); } /** @@ -297,8 +826,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)); } /** @@ -308,12 +836,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); } /** @@ -333,8 +856,47 @@ 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); + } + + /** + * 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) { + try { + return getReadableChannel(self, fdAccess).read(buffer); + } catch (ClosedByInterruptException e) { + return ioStatusSync.UNAVAILABLE; + } catch (NonReadableChannelException e) { + throw Throw.throwNonReadable(context); + } catch (IOException e) { + throw Throw.throwIOException(e, context); + } + } + + /** + * @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(); + } } /** @@ -346,11 +908,28 @@ 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); + 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 { - throw Throw.throwNonReadable(context); + byte[] buf = new byte[length]; + int ret = readBytes(self, fdAccess, buf, 0, length); + nativeMemory.writeMemory(addr, length, buf); + return ret; } } @@ -365,8 +944,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)); } /** @@ -377,22 +955,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); } } @@ -407,8 +981,7 @@ 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)); } /** @@ -419,15 +992,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); } } @@ -444,8 +1013,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); } /** @@ -457,15 +1025,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); } } @@ -496,6 +1060,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); @@ -559,6 +1127,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; @@ -596,8 +1182,16 @@ 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_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); @@ -611,6 +1205,11 @@ 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_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); @@ -620,6 +1219,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 +1283,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. @@ -715,7 +1321,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); @@ -740,6 +1346,131 @@ 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))); + } + + private ReadableByteChannel getReadableChannel(int fd) { + Channel channel = Checks.ensureOpen(getChannel(fd), getContext()); + if (channel instanceof ReadableByteChannel readableByteChannel) { + return readableByteChannel; + } + 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))); + } + + 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 { @@ -762,6 +1493,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"); @@ -806,5 +1545,47 @@ 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); + } + } + + 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..148bc3a34f9f --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/InformationLeak.java @@ -0,0 +1,145 @@ +/* + * 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 java.util.Optional; + +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 + * 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 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); + } + } + + 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); + } + } + + public boolean istty() { + return context.getEnv().in() == System.in && context.getEnv().out() == System.out; + } + + @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); + } + } + + @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/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/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 new file mode 100644 index 000000000000..910dd25a2eb1 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsMeta.java @@ -0,0 +1,264 @@ +/* + * 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.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; +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; +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 + // 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; + 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; + + // 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; + + // 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 + public EspressoContext getContext() { + return context; + } + + public Meta getMeta() { + return meta; + } + + 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); + 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); + + // 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; + + // 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) { + return meta.knownKlass(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); + } + + } + + 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/LibsState.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsState.java index 98f486ec9e3f..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 @@ -22,17 +22,48 @@ */ 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.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; +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; 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); + 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); } @@ -49,10 +80,202 @@ 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 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; + 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); + } + + 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/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/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 + } +} 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_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/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..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 @@ -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,40 @@ 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); + 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))); + } + + 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 39ea2333f624..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 @@ -29,6 +29,8 @@ 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.JNU; import com.oracle.truffle.espresso.libs.libjava.LibJava; import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.runtime.EspressoContext; @@ -77,7 +79,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, @Inject JNU jnu) { // Import properties from host. Props props = new Props(ctx); String[] known = new String[props.fixedLength]; @@ -85,9 +87,13 @@ 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.fileEncodingNdx] = java.lang.System.getProperty("file.encoding"); - + known[props.sunJnuEncodingNdx] = jnu.getCharSet().toString(); + 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"); @@ -95,8 +101,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"); @@ -143,6 +149,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 +170,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 +195,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/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_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_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/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/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/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_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..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 @@ -25,9 +25,18 @@ import java.io.FileDescriptor; import java.io.IOException; +import com.oracle.truffle.espresso.ffi.NativeAccess; +import com.oracle.truffle.espresso.ffi.RawPointer; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; +import com.oracle.truffle.espresso.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; @@ -35,22 +44,46 @@ @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); + } + NativeMemory nativeMemory = ctx.getNativeAccess().nativeMemory(); + long addr = nativeMemory.allocateMemory(length); + long oldPosition = io.position(fd, FDAccess.forFileDescriptor()); + assert oldPosition >= 0; + try { + io.seek(fd, FDAccess.forFileDescriptor(), position); + io.readAddress(fd, FDAccess.forFileDescriptor(), addr, Math.toIntExact(length)); + } finally { + io.seek(fd, FDAccess.forFileDescriptor(), oldPosition); + } + return addr; } @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 @@ -72,9 +105,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 < 0) { + return io.position(fd, FDAccess.forFileDescriptor()); + } + io.seek(fd, FDAccess.forFileDescriptor(), offset); + return 0; } @Substitution @@ -107,16 +144,16 @@ 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) { + return io.readAddress(fd, FDAccess.forFileDescriptor(), address, len); + } @Substitution @@ -128,9 +165,8 @@ 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) { + return io.writeAddress(fd, FDAccess.forFileDescriptor(), address, len); } @Substitution @@ -143,15 +179,22 @@ 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 @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. + long oldPos = io.position(fd, FDAccess.forFileDescriptor()); + try { + io.seek(fd, FDAccess.forFileDescriptor(), position); + return io.readAddress(fd, FDAccess.forFileDescriptor(), address, len); + } finally { + io.seek(fd, FDAccess.forFileDescriptor(), oldPos); + } } @Substitution @@ -174,4 +217,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_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..e8b1acf42ff3 --- /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,404 @@ +/* + * 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 java.nio.channels.SelectionKey; +import java.nio.channels.Selector; + +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 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: + 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/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..c027bab7f0ef --- /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,72 @@ +/* + * 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 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) { + 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) { + 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) { + 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 + return Math.toIntExact(io.writev(fd, FDAccess.forFileDescriptor(), address, len)); + } + + @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_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); + } + +} 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/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(); - } } 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..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,22 +22,22 @@ */ 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.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.VersionFilter; /** * EspressoLibs substitution for the Inflater class. The implementation associates a HostInflater @@ -73,18 +73,25 @@ public static void setDictionary(long addr, @JavaType(byte[].class) StaticObject libsState.getInflater(addr).setDictionary(byteArrayB, off, len); } - @Substitution(languageFilter = VersionFilter.Java11OrLater.class) - 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 + @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, 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); @@ -95,23 +102,25 @@ 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); } 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 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); @@ -119,103 +128,71 @@ 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) { - 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 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); } 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 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) { - 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 @@ -259,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/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..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 @@ -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; @@ -102,6 +99,9 @@ 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.JNU; +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 +203,9 @@ public final class EspressoContext implements RuntimeAccess 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/runtime/jimage/BasicImageReader.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/jimage/BasicImageReader.java index d8aadbf9ba09..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 @@ -59,7 +59,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 lastRead; + map = ByteBuffer.allocateDirect(Math.toIntExact(channel.size())); + do { + lastRead = channel.read(map); + } while (lastRead >= 0 && map.hasRemaining()); + } int headerSize = ImageHeader.getHeaderSize(); if (map.capacity() < headerSize) { 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/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/VersionFilter.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/VersionFilter.java index c2e6588d6834..c96880037a46 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/VersionFilter.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/VersionFilter.java @@ -225,4 +225,16 @@ public boolean isValidFor(JavaVersion version) { return version.java25OrLater(); } } + + final class Java9To21 implements VersionFilter { + public static final Java9To21 INSTANCE = new Java9To21(); + + private Java9To21() { + } + + @Override + public boolean isValidFor(JavaVersion version) { + return version.java21OrEarlier() && version.java9OrLater(); + } + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_lang_StackStreamFactory_AbstractStackWalker.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_lang_StackStreamFactory_AbstractStackWalker.java index e08a30fe0009..b706fb667102 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_lang_StackStreamFactory_AbstractStackWalker.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_lang_StackStreamFactory_AbstractStackWalker.java @@ -82,4 +82,15 @@ public static int fetchStackFrames( @Inject VM vm) { return vm.JVM_MoreStackWalk(stream, mode, anchor, bufferSize - startIndex, startIndex, frames, lang, meta); } + + @Substitution(hasReceiver = true, languageFilter = VersionFilter.Java9To21.class) + public static int fetchStackFrames( + @JavaType(internalName = "Ljava/lang/StackStreamFactory;") StaticObject stream, + long mode, long anchor, int batchsize, int startIndex, + @JavaType(Object[].class) StaticObject frames, + @Inject EspressoLanguage lang, + @Inject Meta meta, + @Inject VM vm) { + return vm.JVM_MoreStackWalk(stream, mode, anchor, batchsize, startIndex, frames, lang, meta); + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_lang_VirtualThread.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_lang_VirtualThread.java index 46f96b92eba5..1ced141bf23d 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_lang_VirtualThread.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_lang_VirtualThread.java @@ -24,6 +24,7 @@ import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions; import com.oracle.truffle.espresso.substitutions.Substitution; +import com.oracle.truffle.espresso.substitutions.libs.EspressoLibsFilter; @EspressoSubstitutions public final class Target_java_lang_VirtualThread { @@ -34,4 +35,9 @@ private Target_java_lang_VirtualThread() { public static void unblockVirtualThreads() { // no-op: loom continuations are not supported } + + @Substitution(languageFilter = EspressoLibsFilter.class) + public static void registerNatives() { + // no-op: we currently don't support virtual Threads + } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_lang_reflect_Array.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_lang_reflect_Array.java index de6339d35bf7..aa6c4634487b 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_lang_reflect_Array.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_java_lang_reflect_Array.java @@ -44,7 +44,9 @@ 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.Throws; import com.oracle.truffle.espresso.vm.InterpreterToVM; +import com.oracle.truffle.espresso.vm.VM; @EspressoSubstitutions public final class Target_java_lang_reflect_Array { @@ -759,6 +761,12 @@ public static void set(@JavaType(Object.class) StaticObject array, int index, @J } } + @Substitution + @Throws(IllegalArgumentException.class) + public static int getLength(@JavaType(Object.class) StaticObject array, @Inject VM vm, @Inject EspressoLanguage language, @Inject SubstitutionProfiler profiler) { + return vm.JVM_GetArrayLength(array, language, profiler); + } + private static int getForeignArrayLength(StaticObject array, EspressoLanguage language, Meta meta) { assert array.isForeignObject(); try { 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_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..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 @@ -40,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; @@ -52,8 +49,8 @@ import com.oracle.truffle.espresso.classfile.JavaVersion; import com.oracle.truffle.espresso.classfile.descriptors.Name; import com.oracle.truffle.espresso.classfile.descriptors.Symbol; -import com.oracle.truffle.espresso.ffi.Buffer; -import com.oracle.truffle.espresso.ffi.RawPointer; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory; +import com.oracle.truffle.espresso.ffi.memory.NativeMemory.MemoryAccessMode; import com.oracle.truffle.espresso.impl.ArrayKlass; import com.oracle.truffle.espresso.impl.ClassRegistry; import com.oracle.truffle.espresso.impl.EspressoClassLoadingException; @@ -87,7 +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; @@ -441,32 +440,23 @@ 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 */ @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"); } - @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; } /** @@ -479,9 +469,7 @@ public static long allocateMemory(@SuppressWarnings("unused") @JavaType(Unsafe.c * * @throws IllegalArgumentException if the size is negative or too large for the native size_t * type - * * @throws OutOfMemoryError if the allocation is refused by the system - * * @see #allocateMemory */ @TruffleBoundary @@ -490,18 +478,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 +495,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) @@ -541,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) @@ -590,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); } /** @@ -768,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) @@ -865,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) @@ -945,6 +947,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 +962,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 +985,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 +997,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,11 +1009,13 @@ 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); } } - /** @see GetByteWithBase */ + /** + * @see GetByteWithBase + */ @GenerateInline(false) // not available in substitutions @Substitution(hasReceiver = true) abstract static class PutInt extends UnsafeAccessNode { @@ -1007,7 +1024,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 +1036,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 +1048,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 +1060,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 +1080,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 +1165,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 +1209,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 +1253,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 +1297,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 +1341,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 +1385,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 +1429,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 +1479,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 +1523,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,8 +1568,14 @@ 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 +1625,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,9 +1672,16 @@ 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 +1723,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 +1770,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,9 +1817,14 @@ 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)") @@ -1764,9 +1864,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 +1911,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 +1958,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 +2005,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 +2056,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 +2103,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 +2155,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 +2202,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 +2249,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,9 +2296,14 @@ 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)") @@ -2191,9 +2343,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 +2390,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 +2437,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 +2490,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 +2502,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,11 +2514,13 @@ 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); } } - /** @see GetByteWithBase */ + /** + * @see GetByteWithBase + */ @GenerateInline(false) // not available in substitutions @Substitution(hasReceiver = true) abstract static class GetInt extends UnsafeAccessNode { @@ -2360,7 +2529,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 +2541,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 +2553,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 +2565,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 +2579,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 +2624,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) { + 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(unwrapNullOrArray(getLanguage(), holder), offset, value); + UnsafeAccess.getIfAllowed(getMeta()).putObjectVolatile(unwrapArray(getLanguage(), holder), offset, value); } @Specialization(guards = "!isNullOrArray(holder)") @@ -2491,9 +2671,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 +2715,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 +2759,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 +2803,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 +2847,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 +2891,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 +2935,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 +2984,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 +3032,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 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(unwrapNullOrArray(getLanguage(), holder), offset, before, after); + return UnsafeAccess.getIfAllowed(getMeta()).compareAndSwapInt(unwrapArray(getLanguage(), holder), offset, before, after); } @Specialization(guards = "!isNullOrArray(holder)") @@ -2861,10 +3088,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 UnsafeAccess.getIfAllowed(getMeta()).compareAndSwapLong(unwrapNullOrArray(getLanguage(), holder), offset, before, 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(unwrapArray(getLanguage(), holder), offset, before, after); } @Specialization(guards = "!isNullOrArray(holder)") @@ -2926,13 +3159,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 +3217,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)") + @Specialization(guards = "isNull(holder)") + int doNull(@SuppressWarnings("unused") @JavaType(Unsafe.class) StaticObject self, @JavaType(Object.class) StaticObject holder, long offset, + int before, int 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) { - UnsafeAccess.checkAllowed(getMeta()); - return UnsafeSupport.compareAndExchangeInt(unwrapNullOrArray(getLanguage(), holder), offset, before, after); + return UnsafeSupport.compareAndExchangeInt(unwrapArray(getLanguage(), holder), offset, before, after); } @Specialization(guards = "!isNullOrArray(holder)") 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) { } } 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: *
    diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/StackWalk.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/StackWalk.java index aff275ecc69b..803e1e43ba94 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/StackWalk.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/vm/StackWalk.java @@ -75,19 +75,20 @@ static boolean needMethodInfo(long mode) { return (mode & FILL_CLASS_REFS_ONLY) == 0; } - private static boolean synchronizedConstants(Meta meta) { + public static boolean synchronizedConstants(Meta meta) { + boolean check = true; Klass stackStreamFactory = meta.java_lang_StackStreamFactory; StaticObject statics = stackStreamFactory.tryInitializeAndGetStatics(); - assert DEFAULT_MODE == getConstantField(stackStreamFactory, statics, "DEFAULT_MODE", meta); + check &= DEFAULT_MODE == getConstantField(stackStreamFactory, statics, "DEFAULT_MODE", meta); if (meta.getJavaVersion().java21OrEarlier()) { - assert FILL_CLASS_REFS_ONLY == getConstantField(stackStreamFactory, statics, "FILL_CLASS_REFS_ONLY", meta); - assert GET_CALLER_CLASS == getConstantField(stackStreamFactory, statics, "GET_CALLER_CLASS", meta); + check &= FILL_CLASS_REFS_ONLY == getConstantField(stackStreamFactory, statics, "FILL_CLASS_REFS_ONLY", meta); + check &= GET_CALLER_CLASS == getConstantField(stackStreamFactory, statics, "GET_CALLER_CLASS", meta); } else { - assert FILL_CLASS_REFS_ONLY == getConstantField(stackStreamFactory, statics, "CLASS_INFO_ONLY", meta); + check &= FILL_CLASS_REFS_ONLY == getConstantField(stackStreamFactory, statics, "CLASS_INFO_ONLY", meta); } - assert SHOW_HIDDEN_FRAMES == getConstantField(stackStreamFactory, statics, "SHOW_HIDDEN_FRAMES", meta); - assert FILL_LIVE_STACK_FRAMES == getConstantField(stackStreamFactory, statics, "FILL_LIVE_STACK_FRAMES", meta); - return true; + check &= SHOW_HIDDEN_FRAMES == getConstantField(stackStreamFactory, statics, "SHOW_HIDDEN_FRAMES", meta); + check &= FILL_LIVE_STACK_FRAMES == getConstantField(stackStreamFactory, statics, "FILL_LIVE_STACK_FRAMES", meta); + return check; } private static int getConstantField(Klass stackStreamFactory, StaticObject statics, String name, Meta meta) { 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); + } } } diff --git a/vm/ci/ci_common/common-runspec.jsonnet b/vm/ci/ci_common/common-runspec.jsonnet index 11fc8423a125..5b693b5272cd 100644 --- a/vm/ci/ci_common/common-runspec.jsonnet +++ b/vm/ci/ci_common/common-runspec.jsonnet @@ -216,7 +216,7 @@ local evaluate_late(key, object) = task_spec(run_spec.evaluate_late({key:object} # Deploy it to the artifact server self.mx_vm_espresso + deploy_artifacts(self.os, suite='espresso', tags=['standalone']), ], - }) + timelimit('1:45:00') + notify_emails('gilles.m.duboscq@oracle.com'), + }) + timelimit('1:45:00') + task_spec(graal_common.deps.espresso) + notify_emails('gilles.m.duboscq@oracle.com'), local deploy_vm_base_task_dict = { # diff --git a/vm/ci/ci_common/common.jsonnet b/vm/ci/ci_common/common.jsonnet index c580010412ac..b29ab895c792 100644 --- a/vm/ci/ci_common/common.jsonnet +++ b/vm/ci/ci_common/common.jsonnet @@ -158,7 +158,7 @@ local devkits = graal_common.devkits; $.mx_vm_complete + self.artifact_deploy_standalones_dry_run(os) ], - full_vm_build: graal_common.deps.svm + graal_common.deps.sulong + graal_common.deps.truffleruby + graal_common.deps.graalpy + graal_common.deps.fastr + vm.custom_vm, + full_vm_build: graal_common.deps.svm + graal_common.deps.sulong + graal_common.deps.truffleruby + graal_common.deps.graalpy + graal_common.deps.fastr + vm.custom_vm + graal_common.deps.espresso, graalvm_complete_build_deps(edition, os, arch, java_version, espresso_java_version=25, espresso_extra_java_version=[21]): local java_deps(edition) = { 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.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..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 @@ -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; @@ -106,29 +107,40 @@ 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. - */ - 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); + 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]; + 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); + } catch (ArrayIndexOutOfBoundsException e) { + if (WasmGCUnsafeSupport.includeErrorMessage()) { + WasmGCUnsafeSupport.fatalAccessError(o, e.toString(), offset, true); } - }; - int rightShift = 8 * elementByteOffset(scaledOffset, indexScale); - return (byte) (longBits >> rightShift); + throw e; + } } @SubstrateForeignCallTarget(stubCallingConvention = false) @@ -208,21 +220,35 @@ 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, false); + } + throw new UnsupportedOperationException(); } - throw new UnsupportedOperationException(); } + } catch (ArrayIndexOutOfBoundsException e) { + if (WasmGCUnsafeSupport.includeErrorMessage()) { + WasmGCUnsafeSupport.fatalAccessError(o, e.toString(), offset, false); + } + + throw e; } } @@ -343,4 +369,19 @@ private static int getArrayBaseOffset(JavaKind kind) { private static int getArrayIndexScale(JavaKind kind) { return ImageSingletons.lookup(ObjectLayout.class).getArrayIndexScale(kind); } + + /** + * 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); + int indexScale = getArrayIndexScale(o); + int index = scaledOffset / indexScale; + int length = Array.getLength(o); + return index >= length && scaledOffset < ((index * indexScale + 4) & ~0x03); + } } 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..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 @@ -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; @@ -345,20 +347,44 @@ public static Path createTempDirectory(String prefix, FileAttribute... attrs) @TargetClass(java.io.RandomAccessFile.class) @SuppressWarnings("all") final class Target_java_io_RandomAccessFile_Web { + @Alias static int O_RDWR; + @Alias FileChannel channel; @Substitute private void open0(String name, int mode) throws FileNotFoundException { - throw new UnsupportedOperationException("RandomAccessFile.open0"); + 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); + channel = FileChannel.open(Path.of(name), options); + } catch (IOException e) { + throw new RuntimeException(e); + } } @Substitute private int read0() throws IOException { - throw new UnsupportedOperationException("RandomAccessFile.read0"); + 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 { - throw new UnsupportedOperationException("RandomAccessFile.readBytes"); + private int readBytes0(byte[] b, int off, int len) throws IOException { + ByteBuffer buf = ByteBuffer.allocate(len); + int read = channel.read(buf); + buf.flip(); + buf.get(b, off, Math.min(read, len)); + return read; } @Substitute @@ -373,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 { - throw new UnsupportedOperationException("RandomAccessFile.seek0"); + channel.position(pos); } @Substitute public long length() throws IOException { - throw new UnsupportedOperationException("RandomAccessFile.length"); + return channel.size(); } @Substitute @@ -393,7 +419,7 @@ public void setLength(long newLength) throws IOException { @Substitute private static void initIDs() { - throw new UnsupportedOperationException("RandomAccessFile.initIDs"); + // do nothing } } @@ -422,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 {