diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/JavaMainWrapper.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/JavaMainWrapper.java index 298c022c58c7..9b10d8fb7ec1 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/JavaMainWrapper.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/JavaMainWrapper.java @@ -43,11 +43,15 @@ import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.StackValue; import org.graalvm.nativeimage.VMRuntime; +import org.graalvm.nativeimage.c.CContext; import org.graalvm.nativeimage.c.function.CEntryPoint; +import org.graalvm.nativeimage.c.function.CFunction; import org.graalvm.nativeimage.c.function.CFunctionPointer; +import org.graalvm.nativeimage.c.struct.CPointerTo; import org.graalvm.nativeimage.c.struct.SizeOf; import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.c.type.CCharPointerPointer; +import org.graalvm.nativeimage.c.type.CIntPointer; import org.graalvm.nativeimage.c.type.CTypeConversion; import org.graalvm.nativeimage.c.type.CTypeConversion.CCharPointerHolder; import org.graalvm.nativeimage.c.type.WordPointer; @@ -272,7 +276,11 @@ private static int runCore0() { * exceptions in a InvocationTargetException. */ JavaMainSupport mainSupport = ImageSingletons.lookup(JavaMainSupport.class); - invokeMain(mainSupport.mainArgs); + String[] args = mainSupport.mainArgs; + if (Platform.includedIn(Platform.WINDOWS.class)) { + args = WindowsArguments.alterArgs(args); + } + invokeMain(args); return 0; } catch (Throwable ex) { @@ -287,6 +295,88 @@ private static int runCore0() { } } + @CContext(WindowsArguments.Directives.class) + final class WindowsArguments { + + private WindowsArguments() { + } + + public static String[] alterArgs(String[] originalArgs) { + return readCommandLineArgs(); + } + + private static final int WCHAR_SIZE = 2; + + private static String[] readCommandLineArgs() { + var cmd = GetCommandLineW(); + + CIntPointer numOfArgs = StackValue.get(Long.BYTES); + + WCharPointerPointer args = CommandLineToArgvW(cmd, numOfArgs); + try { + var numArgs = numOfArgs.read(); + + var results = new String[numArgs - 1]; + for (var i = 0; i < results.length; i++) { + var arg = args.read(i + 1); + results[i] = toJavaString(arg); + } + + return results; + } finally { + LocalFree(args); + } + } + + private static String toJavaString(WCharPointer arg) { + return CTypeConversion.asByteBuffer(arg, wcslen(arg) * WCHAR_SIZE) + .order(java.nio.ByteOrder.LITTLE_ENDIAN) + .asCharBuffer() + .toString(); + } + + @CPointerTo(nameOfCType = "wchar_t") + private interface WCharPointer extends PointerBase { + } + + @CPointerTo(WCharPointer.class) + private interface WCharPointerPointer extends PointerBase { + + WCharPointer read(int index); + } + + @CFunction + private static native WCharPointer GetCommandLineW(); + + @CFunction + private static native WCharPointerPointer CommandLineToArgvW( + WCharPointer cmdLine, CIntPointer numArgsOut); + + @CFunction + private static native int wcslen(WCharPointer str); + + @CFunction + private static native void LocalFree(PointerBase p); + + static final class Directives implements CContext.Directives { + + @Override + public boolean isInConfiguration() { + return Platform.includedIn(Platform.WINDOWS.class); + } + + @Override + public List getHeaderFiles() { + return List.of("", ""); + } + + @Override + public List getLibraries() { + return List.of("Kernel32", "Shell32"); + } + } + } + @Uninterruptible(reason = "The caller initialized the thread state, so the callees do not need to be uninterruptible.", calleeMustBe = false) private static void runShutdown() { RecurringCallbackSupport.suspendCallbackTimer("Recurring callbacks can't be executed during shutdown."); diff --git a/substratevm/src/native-image-module-tests/hello.app/src/main/java/hello/Main.java b/substratevm/src/native-image-module-tests/hello.app/src/main/java/hello/Main.java index 3fef2eb4738f..e373e0adb490 100644 --- a/substratevm/src/native-image-module-tests/hello.app/src/main/java/hello/Main.java +++ b/substratevm/src/native-image-module-tests/hello.app/src/main/java/hello/Main.java @@ -28,9 +28,10 @@ import java.io.IOException; import java.lang.module.Configuration; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; +import java.util.List; +import java.util.Optional; import java.util.Scanner; import java.util.Set; import java.util.stream.Collectors; @@ -38,7 +39,12 @@ import jdk.dynalink.StandardOperation; public class Main { - public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + public static void main(String[] args) throws Exception { + if (args.length > 0 && "print".equals(args[0])) { + List toPrint = Arrays.asList(args).subList(1, args.length); + System.out.println(toPrint); + System.exit(0); + } failIfAssertionsAreDisabled(); Module helloAppModule = Main.class.getModule(); @@ -62,6 +68,8 @@ public static void main(String[] args) throws NoSuchMethodException, InvocationT /* Use classes from Java module jdk.dynalink (which the builder does NOT depend on) */ System.out.println("jdk.dynalink.StandardOperation enum values: " + String.join(" ", Arrays.stream(StandardOperation.values()).map(StandardOperation::toString).collect(Collectors.joining(" ")))); + + testPassingArguments(); } private static void failIfAssertionsAreDisabled() { @@ -156,4 +164,23 @@ private static void testResourceAccess(Module helloAppModule, Module helloLibMod assert helloLibModuleResourceContents.equals(s.nextLine()) : "Class.getResourceAsStream(String) result differs from Module.getResourceAsStream(String) result"; } } + + private static void testPassingArguments() throws Exception { + Optional cmd = ProcessHandle.current().info().command(); + if (cmd.isEmpty() || cmd.get().endsWith("java") || cmd.get().endsWith("java.exe")) { + return; + } + System.err.println("Now testing passing arguments to " + cmd); + String t1 = "使"; + String t2 = "用"; + String t3 = "者"; + Process proc = new ProcessBuilder(cmd.get(), "print", t1, t2, t3).start(); + int exitCode = proc.waitFor(); + assert exitCode == 0 : "Can't invoke " + cmd.get(); + + List lines = proc.inputReader().lines().toList(); + assert lines.size() == 1 : "Assuming one line " + lines; + // System.err.println("got: " + lines.get(0)); + assert lines.get(0).equals("[" + t1 + ", " + t2 + ", " + t3 + "]"); + } }