Skip to content

Commit

Permalink
Add subtyping to @AutomaticallyRegisteredImageSingleton-generated Fea…
Browse files Browse the repository at this point in the history
…tures

The hierarchy of the generated InternalFeature classes now
mirrors the hierarchy of the singleton classes themselves.
  • Loading branch information
Lukáš Rozsypal committed Sep 12, 2024
1 parent 7a72e15 commit 715a8fe
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
import com.oracle.svm.core.util.TimeUtils;

@AutomaticallyRegisteredImageSingleton(ThreadCpuTimeSupport.class)
final class LinuxThreadCpuTimeSupport implements ThreadCpuTimeSupport {
public class LinuxThreadCpuTimeSupport implements ThreadCpuTimeSupport {

@Override
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@
* annotated class must not rely on other features being registered already, or other image
* singletons being present already.
*
* If both a class and a subclass are annotated with {@link AutomaticallyRegisteredImageSingleton}
* (and the subclass is not disabled by {@link #onlyWith}), only the subclass will be registered as
* an image singleton.
*
* The requirements and restrictions of {@link AutomaticallyRegisteredFeature} apply also to this
* annotation.
*/
Expand All @@ -54,14 +58,17 @@
public @interface AutomaticallyRegisteredImageSingleton {

/**
* The keys under which the singleton is registered in {@link ImageSingletons}. If no keys are
* specified, the annotated class itself is used as the key.
* The keys under which the singleton is registered in {@link ImageSingletons}. If the annotated
* class extends another annotated class, keys from the base class are inherited. If no keys are
* specified (or inherited), the annotated class itself is used as the key.
*/
Class<?>[] value() default {};

/**
* Register only if all provided {@link BooleanSupplier} objects evaluate to true. If there are
* no suppliers, the singleton is registered unconditionally.
*
* The values of this attribute are not inherited; a subclass may have fewer restrictions.
*/
Class<? extends BooleanSupplier>[] onlyWith() default {};
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,30 +26,19 @@

import java.nio.file.Path;

import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.impl.InternalPlatform;

import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
import com.oracle.svm.core.feature.InternalFeature;
import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton;

@Platforms(InternalPlatform.PLATFORM_JNI.class)
@AutomaticallyRegisteredFeature
public final class JDKLibDirectoryFeature implements InternalFeature {
@AutomaticallyRegisteredImageSingleton(JDKLibDirectoryProvider.class)
public class HostJDKLibDirectoryProvider implements JDKLibDirectoryProvider {
@Override
public void duringSetup(DuringSetupAccess access) {
if (!ImageSingletons.contains(JDKLibDirectoryProvider.class)) {
ImageSingletons.add(JDKLibDirectoryProvider.class, new HostJDKLibDirectoryProvider());
}
}

private static final class HostJDKLibDirectoryProvider implements JDKLibDirectoryProvider {
@Override
public Path getJDKLibDirectory() {
/* On Windows, JDK libraries are in `<java.home>\bin` directory. */
boolean isWindows = Platform.includedIn(Platform.WINDOWS.class);
return Path.of(System.getProperty("java.home"), isWindows ? "bin" : "lib");
}
public Path getJDKLibDirectory() {
/* On Windows, JDK libraries are in `<java.home>\bin` directory. */
boolean isWindows = Platform.includedIn(Platform.WINDOWS.class);
return Path.of(System.getProperty("java.home"), isWindows ? "bin" : "lib");
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 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
Expand All @@ -25,6 +25,7 @@
package com.oracle.svm.processor;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
Expand All @@ -35,6 +36,8 @@
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;

import jdk.graal.compiler.processor.AbstractProcessor;
Expand Down Expand Up @@ -83,21 +86,32 @@ private void processElement(TypeElement annotatedType) {
out.println("@Platforms({" + platforms + "})");
}
out.println("@" + getSimpleName(AutomaticallyRegisteredFeatureProcessor.ANNOTATION_CLASS_NAME));
out.println("public final class " + featureClassName + " implements " +
String.join(", ", new String[]{AutomaticallyRegisteredFeatureProcessor.FEATURE_INTERFACE_CLASS_NAME, FEATURE_SINGLETON_NAME, UNSAVED_SINGLETON_NAME}) + " {");
List<TypeElement> singletonSuperclasses = getSingletonSuperclasses(annotatedType);
String supertypes = singletonSuperclasses.isEmpty()
? " implements " + String.join(", ", new String[]{AutomaticallyRegisteredFeatureProcessor.FEATURE_INTERFACE_CLASS_NAME, FEATURE_SINGLETON_NAME, UNSAVED_SINGLETON_NAME})
: " extends " + getPackage(singletonSuperclasses.get(0)).getQualifiedName().toString() + "." + getTypeNameWithEnclosingClasses(singletonSuperclasses.get(0), "Feature");
out.println("public class " + featureClassName + supertypes + " {");
out.println(" @Override");
out.println(" public void afterRegistration(AfterRegistrationAccess access) {");

List<TypeMirror> onlyWithList = getAnnotationValueList(singletonAnnotation, "onlyWith", TypeMirror.class);
if (!onlyWithList.isEmpty()) {
for (var onlyWith : onlyWithList) {
out.println(" if (!new " + onlyWith + "().getAsBoolean()) {");
if (!singletonSuperclasses.isEmpty()) {
out.println(" super.afterRegistration(access);");
}
out.println(" return;");
out.println(" }");
}
}

List<TypeMirror> keysFromAnnotation = getAnnotationValueList(singletonAnnotation, "value", TypeMirror.class);
for (var superclass : singletonSuperclasses) {
AnnotationMirror superclassAnnotation = getAnnotation(superclass, getType(ANNOTATION_CLASS_NAME));
keysFromAnnotation.addAll(getAnnotationValueList(superclassAnnotation, "value", TypeMirror.class));
}

if (keysFromAnnotation.isEmpty()) {
String keyname = "" + annotatedType + ".class";
out.println(" if (ImageSingletons.lookup(" + LAYERED_SINGLETON_INFO + ".class).handledDuringLoading(" + keyname + ")){");
Expand Down Expand Up @@ -130,6 +144,29 @@ private void processElement(TypeElement annotatedType) {
}
}

/**
* Get the inheritance chain from {@code annotatedType} up to (excluding) the first
* non-{@code @AutomaticallyRegisteredImageSingleton} type.
*
* @return a list ordered from the most specific to the least specific, empty if not even the
* direct superclass is annotated
*/
private List<TypeElement> getSingletonSuperclasses(TypeElement annotatedType) {
List<TypeElement> list = new ArrayList<>();
for (TypeElement curr = annotatedType;;) {
TypeMirror next = curr.getSuperclass();
if (next.getKind() != TypeKind.DECLARED) {
break;
}
curr = (TypeElement) ((DeclaredType) next).asElement();
if (getAnnotation(curr, getType(ANNOTATION_CLASS_NAME)) == null) {
break;
}
list.add(curr);
}
return list;
}

/**
* We allow inner classes to be annotated. To make the generated service name unique, we need to
* concatenate the simple names of all outer classes.
Expand Down

0 comments on commit 715a8fe

Please sign in to comment.