Skip to content
Open
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
31 changes: 30 additions & 1 deletion substratevm/mx.substratevm/mx_substratevm.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,16 @@ def _vm_home(config):
def locale_US_args():
return ['-Duser.country=US', '-Duser.language=en']


def _is_post_merge_or_weekly_job():
build_name = mx.get_env('BUILD_NAME', '').lower()
return 'post-merge' in build_name or 'weekly' in build_name


def _should_run_java_desktop_integration():
tags = tuple(Task.tags or ())
return _is_post_merge_or_weekly_job() or any(tag == GraalTags.headless_java_desktop_integration for tag in tags)

class Tags(set):
def __getattr__(self, name):
if name in self:
Expand All @@ -261,6 +271,7 @@ def __getattr__(self, name):
'debuginfotest',
'standalone_pointsto_unittests',
'native_unittests',
'headless_java_desktop_integration',
'build',
'benchmarktest',
"nativeimagehelp",
Expand Down Expand Up @@ -512,6 +523,14 @@ def svm_gate_body(args, tasks):
with native_image_context(IMAGE_ASSERTION_FLAGS):
native_unittests_task(args.extra_image_builder_arguments)

with Task('java.desktop integration tests', tasks, tags=[GraalTags.headless_java_desktop_integration]) as t:
if t and _should_run_java_desktop_integration():
if mx.is_windows():
mx.warn('Headless java.desktop integration test does not run on Windows')
else:
with native_image_context(IMAGE_ASSERTION_FLAGS) as native_image:
java_desktop_integration_task(native_image, args.extra_image_builder_arguments)

with Task('conditional configuration tests', tasks, tags=[GraalTags.condconfig]) as t:
if t:
with native_image_context(IMAGE_ASSERTION_FLAGS) as native_image:
Expand Down Expand Up @@ -737,6 +756,16 @@ def native_unittests_task(extra_build_args=None):
computed = _compute_native_unittest_args(extra_build_args, include_svm_test_features=True)
native_image_context_run(_native_unittest, computed)


def java_desktop_integration_task(native_image, extra_build_args=None):
build_args = _compute_native_unittest_args(extra_build_args, include_svm_test_features=False)
build_args += svm_experimental_options(['-H:Preserve=module=java.desktop'])
_native_unittest(native_image, [
'--test-classes-per-run', '1',
'com.oracle.svm.integrationtest.HeadlessJavaDesktopTest',
'com.oracle.svm.integrationtest.NonHeadlessJavaDesktopTest',
] + build_args)

def conditional_config_task(native_image):
agent_path = build_native_image_agent(native_image)
conditional_config_filter_path = join(svmbuild_dir(), 'conditional-config-filter.json')
Expand Down Expand Up @@ -2529,6 +2558,7 @@ def collector(line):
mx.logvv('Skipping line: ' + line.rstrip())
return collector

symbol_dump_command = ''
if mx.is_windows():
symbol_dump_command = 'dumpbin /SYMBOLS'
elif mx.is_darwin():
Expand All @@ -2537,7 +2567,6 @@ def collector(line):
symbol_dump_command = 'objdump --wide --syms'
else:
mx.abort('gen_fallbacks not supported on ' + sys.platform)
raise AssertionError('unreachable')

seen_gnu_property_type_5_warnings = False
def suppress_gnu_property_type_5_warnings(line):
Expand Down
1 change: 1 addition & 0 deletions substratevm/mx.substratevm/suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -1169,6 +1169,7 @@
],
"requires": [
"java.compiler",
"java.desktop",
"jdk.jfr",
"java.management",
"jdk.management.jfr",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.graalvm.word.impl.Word;

import com.oracle.svm.core.NeverInline;
import com.oracle.svm.core.OS;
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.snippets.KnownIntrinsics;
import com.oracle.svm.shared.util.StringUtil;
Expand Down Expand Up @@ -102,6 +103,9 @@ public void loadLibraryAbsolute(File file) {
if (loadLibrary0(file, false)) {
return;
}
if (loadBuiltinDarwinLibraryAbsoluteFallback(file)) {
return;
}
throw new UnsatisfiedLinkError("Can't load library: " + file);
}

Expand Down Expand Up @@ -151,6 +155,56 @@ private boolean loadLibrary0(File file, boolean builtin) {
}
}

private static String asBuiltinLibraryName(String fileName) {
if (OS.getCurrent() == OS.DARWIN && fileName.startsWith("lib") && fileName.endsWith(".dylib")) {
return fileName.substring("lib".length(), fileName.length() - ".dylib".length());
}
return null;
}

private boolean loadBuiltinDarwinLibraryAbsoluteFallback(File file) {
String builtInName = asBuiltinLibraryName(file.getName());
if (builtInName == null) {
return false;
}
if (file.exists()) {
/*
* Preserve System.load(<absolute path>) semantics for real files. The fallback is only
* intended for builtin JDK libraries whose image/java.home path does not exist as a
* loadable dylib in the generated image.
*/
return false;
}
try {
return isBuiltinDarwinLibraryLocation(file) && addLibrary(builtInName, true);
} catch (IOException e) {
return false;
}
}

private static boolean isBuiltinDarwinLibraryLocation(File file) throws IOException {
if (OS.getCurrent() != OS.DARWIN) {
return false;
}

File canonicalParent = file.getCanonicalFile().getParentFile();
if (canonicalParent == null) {
return false;
}

String imageDirectory = getImageDirectory();
if (imageDirectory != null && canonicalParent.equals(new File(imageDirectory).getCanonicalFile())) {
return true;
}

String javaHome = System.getProperty("java.home");
if (javaHome != null && canonicalParent.equals(new File(javaHome, "lib").getCanonicalFile())) {
return true;
}

return false;
}

protected abstract boolean addLibrary(String canonical, boolean builtin);

public abstract PointerBase findSymbol(String name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ private static String getOnLoadName(String libName, boolean isBuiltIn) {

public boolean fillCGlobalDataMap(Collection<String> staticLibNames) {
List<String> libsWithOnLoad = Arrays.asList("net", "java", "nio", "zip", "sunec", "jaas", "sctp", "extnet",
"j2gss", "j2pkcs11", "j2pcsc", "prefs", "verify", "awt", "awt_xawt", "awt_headless", "lcms",
"fontmanager", "javajpeg", "mlib_image", "attach");
"j2gss", "j2pkcs11", "j2pcsc", "prefs", "verify", "awt", "awt_xawt", "awt_headless", "awt_lwawt",
"lcms", "fontmanager", "javajpeg", "mlib_image", "osxapp", "attach");
// TODO: This check should be removed when all static libs will have JNI_OnLoad function
ArrayList<String> localStaticLibNames = new ArrayList<>(staticLibNames);
localStaticLibNames.retainAll(libsWithOnLoad);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

import static com.oracle.svm.core.jni.access.JNIReflectionDictionary.WRAPPED_CSTRING_EQUIVALENCE;

import java.util.function.Function;
import java.util.function.Supplier;

import org.graalvm.nativeimage.c.function.CFunctionPointer;
import org.graalvm.word.PointerBase;
Expand Down Expand Up @@ -85,10 +85,10 @@ public boolean isBuiltInFunction() {
return (PlatformNativeLibrarySupport.singleton().isBuiltinPkgNative(this.getShortName()));
}

public CGlobalDataInfo getOrCreateBuiltInAddress(Function<String, CGlobalDataInfo> createSymbol) {
public CGlobalDataInfo getOrCreateBuiltInAddress(Supplier<CGlobalDataInfo> createSymbol) {
assert isBuiltInFunction();
if (builtInAddress == null) {
builtInAddress = createSymbol.apply(getShortName());
builtInAddress = createSymbol.get();
}
return builtInAddress;
}
Expand Down Expand Up @@ -156,14 +156,18 @@ public PointerBase getOrFindEntryPoint() {
return entryPoint;
}

private String getShortName() {
public String getShortName() {
StringBuilder sb = new StringBuilder("Java_");
mangleName(getDeclaringClassName(), 1, getDeclaringClassName().length() - 1, sb);
sb.append('_');
mangleName(getName(), 0, name.length(), sb);
return sb.toString();
}

public String getLongName() {
return getShortName() + "__" + getSignature();
}

private String getSignature() {
int closing = getDescriptor().indexOf(')');
assert getDescriptor().startsWith("(") && getDescriptor().indexOf(')') == closing && closing != -1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ private static boolean recommendTraceAgentForAWT() {
if (!ImageSingletons.contains(JNIRegistrationSupport.class) || !ImageSingletons.contains(JNIReflectionDictionary.class)) {
return false;
}
if (!JNIRegistrationSupport.singleton().isRegisteredLibrary("awt")) {
if (!JNIRegistrationSupport.singleton().isAnyLayerRegisteredLibrary("awt")) {
return false; // AWT not used
}
// check if any class located in java.awt or sun.awt is registered for JNI access
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,43 @@
import com.oracle.svm.shared.feature.AutomaticallyRegisteredFeature;
import com.oracle.svm.core.feature.InternalFeature;
import com.oracle.svm.core.jdk.JNIRegistrationUtil;
import com.oracle.svm.core.jdk.NativeLibrarySupport;
import com.oracle.svm.core.jdk.PlatformNativeLibrarySupport;
import com.oracle.svm.shared.singletons.traits.BuiltinTraits.BuildtimeAccessOnly;
import com.oracle.svm.shared.singletons.traits.BuiltinTraits.NoLayeredCallbacks;
import com.oracle.svm.shared.singletons.traits.BuiltinTraits.PartiallyLayerAware;
import com.oracle.svm.shared.singletons.traits.SingletonTraits;
import com.oracle.svm.hosted.FeatureImpl.BeforeImageWriteAccessImpl;
import com.oracle.svm.hosted.c.NativeLibraries;
import com.oracle.svm.util.dynamicaccess.JVMCIRuntimeJNIAccess;

@Platforms({Platform.WINDOWS.class, Platform.LINUX.class})
import jdk.vm.ci.meta.ResolvedJavaMethod;

@Platforms({Platform.WINDOWS.class, Platform.LINUX.class, Platform.DARWIN.class})
@SingletonTraits(access = BuildtimeAccessOnly.class, layeredCallbacks = NoLayeredCallbacks.class, other = PartiallyLayerAware.class)
@AutomaticallyRegisteredFeature
public class JNIRegistrationAWTSupport extends JNIRegistrationUtil implements InternalFeature {
private ResolvedJavaMethod systemLoadMethod;
private boolean headlessJavaDesktopSupportRegistered;

@Override
public void beforeAnalysis(BeforeAnalysisAccess access) {
systemLoadMethod = method(access, "java.lang.System", "load", String.class);
if (isDarwin()) {
registerDarwinBuiltinPkgNatives();
}
if (isLinux() || isDarwin()) {
JNIRegistrationSupport.singleton().addLibraryRegistrationHandler(this::registerHeadlessJavaDesktopSupport);
if (JNIRegistrationSupport.singleton().isPreviousLayerRegisteredLibrary("awt")) {
registerHeadlessJavaDesktopSupport("awt");
}
}
}

@Override
public void afterAnalysis(AfterAnalysisAccess access) {
JNIRegistrationSupport jniRegistrationSupport = JNIRegistrationSupport.singleton();
if (jniRegistrationSupport.isRegisteredLibrary("awt")) {
if (jniRegistrationSupport.isAnyLayerRegisteredLibrary("awt")) {
jniRegistrationSupport.addJvmShimExports(
"JVM_IsStaticallyLinked");
jniRegistrationSupport.addJavaShimExports(
Expand Down Expand Up @@ -85,11 +108,11 @@ public void afterAnalysis(AfterAnalysisAccess access) {
jniRegistrationSupport.registerLibrary("awt_xawt");
}
}
if (jniRegistrationSupport.isRegisteredLibrary("javaaccessbridge")) {
if (jniRegistrationSupport.isAnyLayerRegisteredLibrary("javaaccessbridge")) {
/* Dependency on `jawt` is not expressed in Java, so we register it manually here. */
jniRegistrationSupport.registerLibrary("jawt");
}
if (jniRegistrationSupport.isRegisteredLibrary("javajpeg")) {
if (jniRegistrationSupport.isAnyLayerRegisteredLibrary("javajpeg")) {
jniRegistrationSupport.addJavaShimExports(
"JNU_GetEnv",
"JNU_ThrowByName",
Expand All @@ -100,7 +123,22 @@ public void afterAnalysis(AfterAnalysisAccess access) {

@Override
public void beforeImageWrite(BeforeImageWriteAccess access) {
if (isWindows() && JNIRegistrationSupport.singleton().isRegisteredLibrary("awt")) {
if (isDarwin() && JNIRegistrationSupport.singleton().isAnyLayerRegisteredLibrary("awt")) {
((BeforeImageWriteAccessImpl) access).registerLinkerInvocationTransformer(linkerInvocation -> {
linkerInvocation.addNativeLinkerOption("-Wl,-framework,AppKit");
linkerInvocation.addNativeLinkerOption("-Wl,-framework,Accelerate");
linkerInvocation.addNativeLinkerOption("-Wl,-framework,ApplicationServices");
linkerInvocation.addNativeLinkerOption("-Wl,-framework,CoreText");
linkerInvocation.addNativeLinkerOption("-Wl,-framework,JavaRuntimeSupport");
linkerInvocation.addNativeLinkerOption("-Wl,-framework,QuartzCore");
linkerInvocation.addNativeLinkerOption("-Wl,-framework,Metal");
linkerInvocation.addNativeLinkerOption("-Wl,-framework,OpenGL");
linkerInvocation.addNativeLinkerOption("-Wl,-framework,Security");
linkerInvocation.addNativeLinkerOption("-lc++");
return linkerInvocation;
});
}
if (isWindows() && JNIRegistrationSupport.singleton().isAnyLayerRegisteredLibrary("awt")) {
((BeforeImageWriteAccessImpl) access).registerLinkerInvocationTransformer(linkerInvocation -> {
/*
* Add Windows libraries that are pulled in as a side effect of exporting the
Expand All @@ -112,4 +150,41 @@ public void beforeImageWrite(BeforeImageWriteAccess access) {
});
}
}

private static void registerDarwinBuiltinPkgNatives() {
PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix("java_awt_image");
PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix("java_awt_Font");
PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix("java_awt_Toolkit");
PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix("sun_awt_image");
PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix("sun_awt_CGraphicsDevice");
PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix("sun_awt_CGraphicsEnvironment");
PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix("sun_awt_PlatformGraphicsInfo");
PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix("sun_java2d");
PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix("sun_font");
PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix("sun_lwawt_macosx_LWCToolkit");
PlatformNativeLibrarySupport.singleton().addBuiltinPkgNativePrefix("com_sun_imageio_plugins_jpeg");
}

private void registerHeadlessJavaDesktopSupport(String libname) {
if (!"awt".equals(libname) || headlessJavaDesktopSupportRegistered) {
return;
}
headlessJavaDesktopSupportRegistered = true;
JVMCIRuntimeJNIAccess.register(systemLoadMethod);
if (isDarwin()) {
NativeLibrarySupport.singleton().preregisterUninitializedBuiltinLibrary("awt");
NativeLibrarySupport.singleton().preregisterUninitializedBuiltinLibrary("awt_lwawt");
NativeLibrarySupport.singleton().preregisterUninitializedBuiltinLibrary("osxapp");
NativeLibrarySupport.singleton().preregisterUninitializedBuiltinLibrary("javajpeg");
NativeLibrarySupport.singleton().preregisterUninitializedBuiltinLibrary("lcms");
NativeLibrarySupport.singleton().preregisterUninitializedBuiltinLibrary("mlib_image");
NativeLibrarySupport.singleton().preregisterUninitializedBuiltinLibrary("fontmanager");
}
if (!isDarwin()) {
return;
}
NativeLibraries.singleton().addStaticJniLibrary("awt", "awt_lwawt", "javajpeg", "lcms", "mlib_image");
NativeLibraries.singleton().addStaticJniLibrary("awt_lwawt", "osxapp");
NativeLibraries.singleton().addStaticJniLibrary("fontmanager", "freetype");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;
import java.util.stream.Stream;

import org.graalvm.nativeimage.ImageSingletons;
Expand Down Expand Up @@ -107,6 +108,7 @@ public static class Options {
private NativeLibraries nativeLibraries = null;
private JNIRegistrationSupportSingleton jniRegistrationSupportSingleton = null;
private boolean isSunMSCAPIProviderReachable = false;
private final List<Consumer<String>> libraryRegistrationHandlers = new CopyOnWriteArrayList<>();

public static JNIRegistrationSupport singleton() {
return ImageSingletons.lookup(JNIRegistrationSupport.class);
Expand Down Expand Up @@ -165,10 +167,17 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec
void registerLibrary(String libname) {
if (libname != null && !jniRegistrationSupportSingleton.currentLayerRegisteredLibraries.contains(libname)) {
jniRegistrationSupportSingleton.currentLayerRegisteredLibraries.add(libname);
for (Consumer<String> handler : libraryRegistrationHandlers) {
handler.accept(libname);
}
addLibrary(libname);
}
}

void addLibraryRegistrationHandler(Consumer<String> handler) {
libraryRegistrationHandlers.add(handler);
}

private void addLibrary(String libname) {
/*
* If a library is in our list of static standard libraries, add the library to the linker
Expand All @@ -179,10 +188,18 @@ private void addLibrary(String libname) {
}
}

public boolean isRegisteredLibrary(String libname) {
boolean isCurrentLayerRegisteredLibrary(String libname) {
return jniRegistrationSupportSingleton.currentLayerRegisteredLibraries.contains(libname);
}

boolean isPreviousLayerRegisteredLibrary(String libname) {
return jniRegistrationSupportSingleton.prevLayerRegisteredLibraries.contains(libname);
}

public boolean isAnyLayerRegisteredLibrary(String libname) {
return isCurrentLayerRegisteredLibrary(libname) || isPreviousLayerRegisteredLibrary(libname);
}

/** Adds exports that `jvm` shim should re-export. */
void addJvmShimExports(String... exports) {
addShimExports("jvm", exports);
Expand Down
Loading