Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 52 additions & 4 deletions substratevm/mx.substratevm/mx_substratevm.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@

import sys


if sys.version_info[0] < 3:
from StringIO import StringIO
else:
Expand Down Expand Up @@ -730,13 +731,54 @@ def _cinterfacetutorial(native_image, args=None):
mx.run([join(build_dir, 'cinterfacetutorial')])


def _helloworld(native_image, javac_command, path, build_only, args):
_helloworld_variants = {
'traditional': '''
public class HelloWorld {
public static void main(String[] args) {
System.out.println(System.getenv("%s"));
}
}
''',
'noArgs': '''
// requires JDK 21 and --enable-preview
public class HelloWorld {
static void main() {
System.out.println(System.getenv("%s"));
}
}
''',
'instance': '''
// requires JDK 21 and --enable-preview
class HelloWorld {
void main(String[] args) {
System.out.println(System.getenv("%s"));
}
}
''',
'instanceNoArgs': '''
// requires JDK 21 and --enable-preview
class HelloWorld {
void main() {
System.out.println(System.getenv("%s"));
}
}
''',
'unnamedClass': '''
// requires JDK 21 and javac --enable-preview --source 21 and native-image --enable-preview
void main() {
System.out.println(System.getenv("%s"));
}
''',
}


def _helloworld(native_image, javac_command, path, build_only, args, variant=list(_helloworld_variants.keys())[0]):
mkpath(path)
hello_file = os.path.join(path, 'HelloWorld.java')
envkey = 'HELLO_WORLD_MESSAGE'
output = 'Hello from native-image!'
with open(hello_file, 'w') as fp:
fp.write('public class HelloWorld { public static void main(String[] args) { System.out.println(System.getenv("' + envkey + '")); } }')
fp.write(_helloworld_variants[variant] % envkey)
fp.flush()
mx.run(javac_command + [hello_file])

Expand Down Expand Up @@ -1238,20 +1280,26 @@ def _native_image_configure_extra_jvm_args():

def run_helloworld_command(args, config, command_name):
parser = ArgumentParser(prog='mx ' + command_name)
all_args = ['--output-path', '--javac-command', '--build-only']
all_args = ['--output-path', '--javac-command', '--build-only', '--variant', '--list']
masked_args = [_mask(arg, all_args) for arg in args]
default_variant = list(_helloworld_variants.keys())[0]
parser.add_argument(all_args[0], metavar='<output-path>', nargs=1, help='Path of the generated image', default=[svmbuild_dir(suite)])
parser.add_argument(all_args[1], metavar='<javac-command>', help='A javac command to be used', default=mx.get_jdk().javac)
parser.add_argument(all_args[2], action='store_true', help='Only build the native image')
parser.add_argument(all_args[3], choices=_helloworld_variants.keys(), default=default_variant, help=f'The Hello World source code variant to use (default: {default_variant})')
parser.add_argument(all_args[4], action='store_true', help='Print the Hello World source and exit')
parser.add_argument('image_args', nargs='*', default=[])
parsed = parser.parse_args(masked_args)
javac_command = unmask(parsed.javac_command.split())
output_path = unmask(parsed.output_path)[0]
build_only = parsed.build_only
image_args = unmask(parsed.image_args)
if parsed.list:
mx.log(_helloworld_variants[parsed.variant])
return
native_image_context_run(
lambda native_image, a:
_helloworld(native_image, javac_command, output_path, build_only, a), unmask(image_args),
_helloworld(native_image, javac_command, output_path, build_only, a, variant=parsed.variant), unmask(image_args),
config=config,
)

Expand Down
22 changes: 12 additions & 10 deletions substratevm/mx.substratevm/testhello.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,12 @@ def test():
# expect "#1 0x[0-9a-f]+ in com.oracle.svm.core.code.IsolateEnterStub.JavaMainWrapper_run_.* at [a-z/]+/JavaMainWrapper.java:[0-9]+"
exec_string = execute("backtrace")
stacktraceRegex = [r"#0%shello\.Hello::main%s %s at hello/Hello\.java:77"%(spaces_pattern, param_types_pattern, arg_values_pattern),
r"#1%s%s in com\.oracle\.svm\.core\.JavaMainWrapper::runCore0%s %s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, no_param_types_pattern, no_arg_values_pattern, package_pattern),
r"#2%s%s in com\.oracle\.svm\.core\.JavaMainWrapper::runCore%s %s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, no_param_types_pattern, no_arg_values_pattern, package_pattern),
r"#3%scom\.oracle\.svm\.core\.JavaMainWrapper::doRun%s %s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, param_types_pattern, arg_values_pattern, package_pattern),
r"#4%s(%s in )?com\.oracle\.svm\.core\.JavaMainWrapper::run%s %s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, param_types_pattern, arg_values_pattern, package_pattern),
r"#5%scom\.oracle\.svm\.core\.code\.IsolateEnterStub::JavaMainWrapper_run_%s%s %s"%(spaces_pattern, hex_digits_pattern, param_types_pattern, arg_values_pattern)
r"#1%s%s in com\.oracle\.svm\.core\.JavaMainWrapper::invokeMain%s %s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, param_types_pattern, arg_values_pattern, package_pattern),
r"#2%s(%s in )?com\.oracle\.svm\.core\.JavaMainWrapper::runCore0%s %s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, no_param_types_pattern, no_arg_values_pattern, package_pattern),
r"#3%s%s in com\.oracle\.svm\.core\.JavaMainWrapper::runCore%s %s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, no_param_types_pattern, no_arg_values_pattern, package_pattern),
r"#4%scom\.oracle\.svm\.core\.JavaMainWrapper::doRun%s %s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, param_types_pattern, arg_values_pattern, package_pattern),
r"#5%s(%s in )?com\.oracle\.svm\.core\.JavaMainWrapper::run%s %s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, param_types_pattern, arg_values_pattern, package_pattern),
r"#6%scom\.oracle\.svm\.core\.code\.IsolateEnterStub::JavaMainWrapper_run_%s%s %s"%(spaces_pattern, hex_digits_pattern, param_types_pattern, arg_values_pattern)
]
if musl:
# musl has a different entry point - drop the last two frames
Expand Down Expand Up @@ -364,11 +365,12 @@ def test():
exec_string = execute("backtrace")
stacktraceRegex = [r"#0%shello\.Hello\$Greeter::greeter%s %s at hello/Hello\.java:38"%(spaces_pattern, param_types_pattern, arg_values_pattern),
r"#1%s%s in hello\.Hello::main%s %s at hello/Hello\.java:77"%(spaces_pattern, address_pattern, param_types_pattern, arg_values_pattern),
r"#2%s%s in com\.oracle\.svm\.core\.JavaMainWrapper::runCore0%s %s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, no_param_types_pattern, no_arg_values_pattern, package_pattern),
r"#3%s%s in com\.oracle\.svm\.core\.JavaMainWrapper::runCore%s %s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, no_param_types_pattern, no_arg_values_pattern, package_pattern),
r"#4%scom\.oracle\.svm\.core\.JavaMainWrapper::doRun%s %s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, param_types_pattern, arg_values_pattern, package_pattern),
r"#5%s(%s in )?com\.oracle\.svm\.core\.JavaMainWrapper::run%s %s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, param_types_pattern, arg_values_pattern, package_pattern),
r"#6%scom\.oracle\.svm\.core\.code\.IsolateEnterStub::JavaMainWrapper_run_%s%s %s"%(spaces_pattern, hex_digits_pattern, param_types_pattern, arg_values_pattern)
r"#2%s%s in com\.oracle\.svm\.core\.JavaMainWrapper::invokeMain%s %s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, param_types_pattern, arg_values_pattern, package_pattern),
r"#3%s(%s in )?com\.oracle\.svm\.core\.JavaMainWrapper::runCore0%s %s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, no_param_types_pattern, no_arg_values_pattern, package_pattern),
r"#4%s%s in com\.oracle\.svm\.core\.JavaMainWrapper::runCore%s %s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, no_param_types_pattern, no_arg_values_pattern, package_pattern),
r"#5%scom\.oracle\.svm\.core\.JavaMainWrapper::doRun%s %s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, param_types_pattern, arg_values_pattern, package_pattern),
r"#6%s(%s in )?com\.oracle\.svm\.core\.JavaMainWrapper::run%s %s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, param_types_pattern, arg_values_pattern, package_pattern),
r"#7%scom\.oracle\.svm\.core\.code\.IsolateEnterStub::JavaMainWrapper_run_%s%s %s"%(spaces_pattern, hex_digits_pattern, param_types_pattern, arg_values_pattern)
]
if musl:
# musl has a different entry point - drop the last two frames
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,17 @@

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.BooleanSupplier;

import com.oracle.svm.util.ClassUtil;
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
import org.graalvm.compiler.word.Word;
import org.graalvm.nativeimage.CurrentIsolate;
import org.graalvm.nativeimage.ImageSingletons;
Expand Down Expand Up @@ -74,7 +78,9 @@
import com.oracle.svm.core.thread.ThreadListenerSupport;
import com.oracle.svm.core.thread.VMThreads;
import com.oracle.svm.core.util.CounterSupport;
import com.oracle.svm.core.util.UserError;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.util.ReflectionUtil;

@InternalVMMethod
public class JavaMainWrapper {
Expand All @@ -92,14 +98,41 @@ public class JavaMainWrapper {

public static class JavaMainSupport {

public final MethodHandle javaMainHandle;
private final MethodHandle javaMainHandle;
private final MethodHandle javaMainClassCtorHandle;
final String javaMainClassName;

public String[] mainArgs;

private final boolean mainWithoutArgs;
private final boolean mainNonstatic;

@Platforms(Platform.HOSTED_ONLY.class)
public JavaMainSupport(Method javaMainMethod) throws IllegalAccessException {
this.javaMainHandle = MethodHandles.lookup().unreflect(javaMainMethod);
if (instanceMainMethodSupported()) {
javaMainMethod.setAccessible(true);
int mods = javaMainMethod.getModifiers();
this.mainNonstatic = !Modifier.isStatic(mods);
this.mainWithoutArgs = javaMainMethod.getParameterCount() == 0;
MethodHandle mainHandle = MethodHandles.lookup().unreflect(javaMainMethod);
MethodHandle ctorHandle = null;
if (mainNonstatic) {
// Instance main
try {
Constructor<?> ctor = ReflectionUtil.lookupConstructor(javaMainMethod.getDeclaringClass());
ctorHandle = MethodHandles.lookup().unreflectConstructor(ctor);
} catch (ReflectionUtil.ReflectionUtilError ex) {
throw UserError.abort(ex, "No non-private zero argument constructor found in class %s", ClassUtil.getUnqualifiedName(javaMainMethod.getDeclaringClass()));
}
}
this.javaMainHandle = mainHandle;
this.javaMainClassCtorHandle = ctorHandle;
} else {
this.mainNonstatic = false;
this.mainWithoutArgs = false;
this.javaMainHandle = MethodHandles.lookup().unreflect(javaMainMethod);
this.javaMainClassCtorHandle = null;
}
this.javaMainClassName = javaMainMethod.getDeclaringClass().getName();
}

Expand Down Expand Up @@ -129,6 +162,41 @@ public List<String> getInputArguments() {
}
return Collections.emptyList();
}

}

public static void invokeMain(String[] args) throws Throwable {
JavaMainSupport javaMainSupport = ImageSingletons.lookup(JavaMainSupport.class);
if (javaMainSupport.mainNonstatic) {
Object instance = javaMainSupport.javaMainClassCtorHandle.invoke();
if (javaMainSupport.mainWithoutArgs) {
javaMainSupport.javaMainHandle.invoke(instance);
} else {
javaMainSupport.javaMainHandle.invoke(instance, args);
}
} else {
if (javaMainSupport.mainWithoutArgs) {
javaMainSupport.javaMainHandle.invokeExact();
} else {
javaMainSupport.javaMainHandle.invokeExact(args);
}
}
}

/**
* Determines whether instance main methodes are enabled. See JDK-8306112: Implementation of JEP
* 445: Unnamed Classes and Instance Main Methods (Preview).
*/
public static boolean instanceMainMethodSupported() {
if (JavaVersionUtil.JAVA_SPEC < 21) {
return false;
}
var previewFeature = ReflectionUtil.lookupClass(true, "jdk.internal.misc.PreviewFeatures");
try {
return previewFeature != null && (Boolean) previewFeature.getDeclaredMethod("isEnabled").invoke(null);
} catch (ReflectiveOperationException e) {
throw VMError.shouldNotReachHere(e);
}
}

@Uninterruptible(reason = "The caller initialized the thread state, so the callees do not need to be uninterruptible.", calleeMustBe = false)
Expand Down Expand Up @@ -175,7 +243,7 @@ private static int runCore0() {
* exceptions in a InvocationTargetException.
*/
JavaMainSupport mainSupport = ImageSingletons.lookup(JavaMainSupport.class);
mainSupport.javaMainHandle.invokeExact(mainSupport.mainArgs);
invokeMain(mainSupport.mainArgs);
return 0;
} catch (Throwable ex) {
JavaThreads.dispatchUncaughtException(Thread.currentThread(), ex);
Expand Down
Loading