Skip to content

[GR-65777] Adopt "JDK-8344706: Implement JEP 512: Compact Source Files and Instance Main Methods" #11430

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 18, 2025
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
6 changes: 1 addition & 5 deletions substratevm/mx.substratevm/mx_substratevm.py
Original file line number Diff line number Diff line change
Expand Up @@ -881,33 +881,29 @@ def _cinterfacetutorial(native_image, args=None):
}
''',
'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"));
IO.println(System.getenv("%s"));
}
''',
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,30 +113,22 @@ public static class JavaMainSupport implements ApplicationLayerOnlyImageSingleto

@Platforms(Platform.HOSTED_ONLY.class)
public JavaMainSupport(Method javaMainMethod) throws IllegalAccessException {
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()));
}
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.javaMainHandle = mainHandle;
this.javaMainClassCtorHandle = ctorHandle;
this.javaMainClassName = javaMainMethod.getDeclaringClass().getName();
}

Expand Down Expand Up @@ -199,20 +191,6 @@ public static void invokeMain(String[] args) throws Throwable {
}
}

/**
* Determines whether instance main methodes are enabled. See JDK-8306112: Implementation of JEP
* 445: Unnamed Classes and Instance Main Methods (Preview).
*/
@Platforms(Platform.HOSTED_ONLY.class)
public static boolean instanceMainMethodSupported() {
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)
private static int runCore() {
return runCore0();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.option.HostedOptionKey;
import com.oracle.svm.core.option.SubstrateOptionsParser;
import com.oracle.svm.core.util.BasedOnJDKFile;
import com.oracle.svm.core.util.ExitStatus;
import com.oracle.svm.core.util.InterruptImageBuilding;
import com.oracle.svm.core.util.UserError;
Expand Down Expand Up @@ -495,19 +496,9 @@ private int buildImage(ImageClassLoader classLoader) {
* If no C-level main method was found, look for a Java-level main method
* and use our wrapper to invoke it.
*/
if ("main".equals(mainEntryPointName) && JavaMainWrapper.instanceMainMethodSupported()) {
// Instance main method only supported for "main" method name
if ("main".equals(mainEntryPointName)) {
try {
/*
* JDK-8306112: Implementation of JEP 445: Unnamed Classes and
* Instance Main Methods (Preview)
*
* MainMethodFinder will perform all the necessary checks
*/
String mainMethodFinderClassName = "jdk.internal.misc.MethodFinder";
Class<?> mainMethodFinder = ReflectionUtil.lookupClass(false, mainMethodFinderClassName);
Method findMainMethod = ReflectionUtil.lookupMethod(mainMethodFinder, "findMainMethod", Class.class);
javaMainMethod = (Method) findMainMethod.invoke(null, mainClass);
javaMainMethod = findDefaultJavaMainMethod(mainClass);
} catch (InvocationTargetException ex) {
assert ex.getTargetException() instanceof NoSuchMethodException;
throw UserError.abort(ex.getCause(),
Expand Down Expand Up @@ -618,6 +609,27 @@ private int buildImage(ImageClassLoader classLoader) {
return ExitStatus.OK.getValue();
}

/*
* Finds the main method using the {@code jdk.internal.misc.MethodFinder}.
*
* The {@code MethodFinder} was introduced by JDK-8344706 (Implement JEP 512: Compact Source
* Files and Instance Main Methods) and will perform all the necessary checks.
*/
@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+24/src/java.base/share/classes/jdk/internal/misc/MethodFinder.java#L45-L106")
private static Method findDefaultJavaMainMethod(Class<?> mainClass) throws IllegalAccessException, InvocationTargetException {
Class<?> mainMethodFinder = ReflectionUtil.lookupClass(false, "jdk.internal.misc.MethodFinder");
Method findMainMethod = ReflectionUtil.lookupMethod(mainMethodFinder, "findMainMethod", Class.class);
/*
* We are using Method.invoke and throwing checked exceptions on purpose instead of
* ReflectionUtil to issue a proper error message.
*/
Method invoke = (Method) findMainMethod.invoke(null, mainClass);
/*
* Use ReflectionUtil get a Method object with the right accessibility.
*/
return ReflectionUtil.lookupMethod(invoke.getDeclaringClass(), invoke.getName(), invoke.getParameterTypes());
}

private static void reportConflictingOptions(HostedOptionKey<Boolean> o1, HostedOptionKey<?> o2) {
throw UserError.abort("Cannot pass both options: %s and %s", SubstrateOptionsParser.commandArgument(o1, "+"), SubstrateOptionsParser.commandArgument(o2, "+"));
}
Expand Down