Skip to content

[GR-55278] [GR-58056] Collect all external values for Layered Image #9905

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
Oct 19, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import jdk.graal.compiler.core.test.SubprocessTest;
import org.graalvm.collections.EconomicMap;
import org.junit.Assert;
import org.junit.Test;

import jdk.graal.compiler.core.test.SubprocessTest;
import jdk.graal.compiler.util.ObjectCopier;

/**
Expand Down Expand Up @@ -172,10 +172,10 @@ public void testIt() {
root.put("singleton2", TestObject.TEST_OBJECT_SINGLETON);
root.put("singleton2_2", TestObject.TEST_OBJECT_SINGLETON);

List<Field> externalValues = List.of(ObjectCopier.getField(BaseClass.class, "BASE_SINGLETON"),
List<Field> externalValueFields = List.of(ObjectCopier.getField(BaseClass.class, "BASE_SINGLETON"),
ObjectCopier.getField(TestObject.class, "TEST_OBJECT_SINGLETON"));

String encoded = ObjectCopier.encode(new ObjectCopier.Encoder(externalValues), root);
String encoded = ObjectCopier.encode(new ObjectCopier.Encoder(externalValueFields), root);
if (DEBUG) {
System.out.printf("encoded:%n%s%n", encoded);
}
Expand All @@ -184,7 +184,7 @@ public void testIt() {
System.out.printf("root:%n%s%n", root);
System.out.printf("decoded:%n%s%n", decoded);
}
String reencoded = ObjectCopier.encode(new ObjectCopier.Encoder(externalValues), decoded);
String reencoded = ObjectCopier.encode(new ObjectCopier.Encoder(externalValueFields), decoded);
if (DEBUG) {
System.out.printf("reencoded:%n%s%n", reencoded);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,37 +24,29 @@
*/
package jdk.graal.compiler.hotspot.libgraal;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.stream.Stream;

import jdk.graal.compiler.core.common.spi.ForeignCallSignature;
import jdk.graal.compiler.core.target.Backend;
import jdk.graal.compiler.hotspot.HotSpotForeignCallLinkage;
import jdk.graal.compiler.hotspot.meta.HotSpotHostForeignCallsProvider;
import jdk.graal.compiler.truffle.hotspot.HotSpotTruffleCompilerImpl;
import org.graalvm.collections.EconomicMap;
import org.graalvm.collections.MapCursor;
import org.graalvm.word.LocationIdentity;

import jdk.graal.compiler.core.common.spi.ForeignCallSignature;
import jdk.graal.compiler.core.target.Backend;
import jdk.graal.compiler.debug.GraalError;
import jdk.graal.compiler.hotspot.EncodedSnippets;
import jdk.graal.compiler.hotspot.HotSpotForeignCallLinkage;
import jdk.graal.compiler.hotspot.HotSpotGraalCompiler;
import jdk.graal.compiler.hotspot.HotSpotGraalRuntimeProvider;
import jdk.graal.compiler.hotspot.HotSpotReplacementsImpl;
import jdk.graal.compiler.hotspot.SymbolicSnippetEncoder;
import jdk.graal.compiler.hotspot.meta.HotSpotHostForeignCallsProvider;
import jdk.graal.compiler.hotspot.meta.HotSpotProviders;
import jdk.graal.compiler.options.OptionValues;
import jdk.graal.compiler.truffle.hotspot.HotSpotTruffleCompilerImpl;
import jdk.graal.compiler.util.ObjectCopier;
import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;

Expand Down Expand Up @@ -84,13 +76,13 @@ public static void main(String[] args) throws Exception {

List<ForeignCallSignature> foreignCallSignatures = getForeignCallSignatures(replacements, options, graalRuntime);
EncodedSnippets encodedSnippets = getEncodedSnippets(replacements, options);
List<Field> externalValues = getExternalValues();
List<Field> externalValueFields = ObjectCopier.getExternalValueFields();

EconomicMap<String, Object> encodedObjects = EconomicMap.create();
encodedObjects.put("encodedSnippets", encodedSnippets);
encodedObjects.put("foreignCallSignatures", foreignCallSignatures);

ObjectCopier.Encoder encoder = new ObjectCopier.Encoder(externalValues) {
ObjectCopier.Encoder encoder = new ObjectCopier.Encoder(externalValueFields) {
@Override
protected ClassInfo makeClassInfo(Class<?> declaringClass) {
ClassInfo ci = ClassInfo.of(declaringClass);
Expand Down Expand Up @@ -152,77 +144,4 @@ private static void collectForeignCalls(HotSpotHostForeignCallsProvider foreignC
}
});
}

private static List<Field> getExternalValues() throws IOException {
List<Field> externalValues = new ArrayList<>();
addImmutableCollectionsFields(externalValues);
addStaticFinalObjectFields(LocationIdentity.class, externalValues);

try (FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), Collections.emptyMap())) {
for (String module : List.of("jdk.internal.vm.ci", "jdk.graal.compiler", "com.oracle.graal.graal_enterprise")) {
Path top = fs.getPath("/modules/" + module);
try (Stream<Path> files = Files.find(top, Integer.MAX_VALUE, (path, attrs) -> attrs.isRegularFile())) {
files.forEach(p -> {
String fileName = p.getFileName().toString();
if (fileName.endsWith(".class") && !fileName.equals("module-info.class")) {
// Strip module prefix and convert to dotted form
int nameCount = p.getNameCount();
String className = p.subpath(2, nameCount).toString().replace('/', '.');
// Strip ".class" suffix
className = className.replace('/', '.').substring(0, className.length() - ".class".length());
try {
Class<?> graalClass = Class.forName(className);
addStaticFinalObjectFields(graalClass, externalValues);
} catch (ClassNotFoundException e) {
throw new GraalError(e);
}
}
});
}
}
}
return externalValues;
}

/**
* Adds the static, final, non-primitive fields of non-enum {@code declaringClass} to
* {@code fields}. In the process, the fields are made {@linkplain Field#setAccessible
* accessible}.
*/
private static void addStaticFinalObjectFields(Class<?> declaringClass, List<Field> fields) {
if (Enum.class.isAssignableFrom(declaringClass)) {
return;
}
for (Field field : declaringClass.getDeclaredFields()) {
int fieldModifiers = field.getModifiers();
int fieldMask = Modifier.STATIC | Modifier.FINAL;
if ((fieldModifiers & fieldMask) != fieldMask) {
continue;
}
if (field.getType().isPrimitive()) {
continue;
}
field.setAccessible(true);
fields.add(field);
}
}

/**
* Adds the EMPTY* fields from {@code java.util.ImmutableCollections} to {@code fields}, making
* them {@linkplain Field#setAccessible accessible} in the process.
*/
private static void addImmutableCollectionsFields(List<Field> fields) {
Class<?> c = List.of().getClass().getDeclaringClass();
GraalError.guarantee(c.getName().equals("java.util.ImmutableCollections"), "Incompatible ImmutableCollections class");
for (Field f : c.getDeclaredFields()) {
if (f.getName().startsWith("EMPTY")) {
int modifiers = f.getModifiers();
GraalError.guarantee(Modifier.isStatic(modifiers), "Expect %s to be static", f);
GraalError.guarantee(Modifier.isFinal(modifiers), "Expect %s to be final", f);
GraalError.guarantee(!f.getType().isPrimitive(), "Expect %s to be non-primitive", f);
f.setAccessible(true);
fields.add(f);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import jdk.graal.compiler.debug.GraalError;
import jdk.graal.compiler.graph.Node;
import jdk.graal.compiler.graph.NodeClass;

import jdk.vm.ci.meta.Assumptions;
import jdk.vm.ci.meta.ResolvedJavaMethod;

Expand Down Expand Up @@ -139,6 +138,10 @@ public Object getObject(int i) {
return objects[i];
}

public void setObject(int i, Object object) {
objects[i] = object;
}

public NodeClass<?>[] getNodeClasses() {
return types;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,18 @@
package jdk.graal.compiler.util;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
Expand Down Expand Up @@ -785,16 +792,30 @@ public static class Encoder extends ObjectCopier {

/**
* Map from values to static final fields. In a serialized object graph, references to such
* values are encoded with a reference to the field.
* values are encoded using the static final field they come from. This field is then looked
* up via reflection when the value needs to be decoded.
*/
final Map<Object, Field> externalValues = new IdentityHashMap<>();
final Map<Object, Field> externalValues;

public Encoder(List<Field> externalValues) {
public Encoder(List<Field> externalValueFields) {
this(gatherExternalValues(externalValueFields));
}

/**
* Use precomputed {@code externalValues} to avoid recomputing them.
*/
public Encoder(Map<Object, Field> externalValues) {
objects.add(null);
objectToId.put(null, new ObjectID(0, null));
for (Field f : externalValues) {
addExternalValue(f);
this.externalValues = externalValues;
}

public static Map<Object, Field> gatherExternalValues(List<Field> externalValueFields) {
Map<Object, Field> result = new IdentityHashMap<>();
for (Field f : externalValueFields) {
addExternalValue(result, f);
}
return result;
}

/**
Expand All @@ -808,7 +829,7 @@ protected ClassInfo makeClassInfo(Class<?> declaringClass) {
return ClassInfo.of(declaringClass);
}

private void addExternalValue(Field field) {
private static void addExternalValue(Map<Object, Field> externalValues, Field field) {
GraalError.guarantee(Modifier.isStatic(field.getModifiers()), "Field '%s' is not static. Only a static field can be used as known location for an instance.", field);
Object value = readField(field, null);
if (value == null) {
Expand All @@ -825,7 +846,7 @@ private void addExternalValue(Field field) {
}

public Map<Object, Field> getExternalValues() {
return externalValues;
return Collections.unmodifiableMap(externalValues);
}

private String encodeMap(UnmodifiableEconomicMap<?, ?> map) {
Expand Down Expand Up @@ -1000,6 +1021,79 @@ public static Field getField(Class<?> declaredClass, String fieldName) {
}
}

public static List<Field> getExternalValueFields() throws IOException {
List<Field> externalValues = new ArrayList<>();
addImmutableCollectionsFields(externalValues);
addStaticFinalObjectFields(LocationIdentity.class, externalValues);

try (FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), Collections.emptyMap())) {
for (String module : List.of("jdk.internal.vm.ci", "jdk.graal.compiler", "com.oracle.graal.graal_enterprise")) {
Path top = fs.getPath("/modules/" + module);
try (Stream<Path> files = Files.find(top, Integer.MAX_VALUE, (path, attrs) -> attrs.isRegularFile())) {
files.forEach(p -> {
String fileName = p.getFileName().toString();
if (fileName.endsWith(".class") && !fileName.equals("module-info.class")) {
// Strip module prefix and convert to dotted form
int nameCount = p.getNameCount();
String className = p.subpath(2, nameCount).toString().replace('/', '.');
// Strip ".class" suffix
className = className.replace('/', '.').substring(0, className.length() - ".class".length());
try {
Class<?> graalClass = Class.forName(className);
addStaticFinalObjectFields(graalClass, externalValues);
} catch (ClassNotFoundException e) {
throw new GraalError(e);
}
}
});
}
}
}
return externalValues;
}

/**
* Adds the static, final, non-primitive fields of non-enum {@code declaringClass} to
* {@code fields}. In the process, the fields are made {@linkplain Field#setAccessible
* accessible}.
*/
public static void addStaticFinalObjectFields(Class<?> declaringClass, List<Field> fields) {
if (Enum.class.isAssignableFrom(declaringClass)) {
return;
}
for (Field field : declaringClass.getDeclaredFields()) {
int fieldModifiers = field.getModifiers();
int fieldMask = Modifier.STATIC | Modifier.FINAL;
if ((fieldModifiers & fieldMask) != fieldMask) {
continue;
}
if (field.getType().isPrimitive()) {
continue;
}
field.setAccessible(true);
fields.add(field);
}
}

/**
* Adds the EMPTY* fields from {@code java.util.ImmutableCollections} to {@code fields}, making
* them {@linkplain Field#setAccessible accessible} in the process.
*/
private static void addImmutableCollectionsFields(List<Field> fields) {
Class<?> c = List.of().getClass().getDeclaringClass();
GraalError.guarantee(c.getName().equals("java.util.ImmutableCollections"), "Incompatible ImmutableCollections class");
for (Field f : c.getDeclaredFields()) {
if (f.getName().startsWith("EMPTY")) {
int modifiers = f.getModifiers();
GraalError.guarantee(Modifier.isStatic(modifiers), "Expect %s to be static", f);
GraalError.guarantee(Modifier.isFinal(modifiers), "Expect %s to be final", f);
GraalError.guarantee(!f.getType().isPrimitive(), "Expect %s to be non-primitive", f);
f.setAccessible(true);
fields.add(f);
}
}
}

/**
* Describes the path from a root object to a target object. That is, the sequence of field and
* array reads performed on the root object to access the target object.
Expand Down
Loading