2525package jdk .graal .compiler .util ;
2626
2727import java .io .ByteArrayOutputStream ;
28+ import java .io .IOException ;
2829import java .io .PrintStream ;
2930import java .lang .reflect .Array ;
3031import java .lang .reflect .Field ;
3132import java .lang .reflect .Modifier ;
33+ import java .net .URI ;
34+ import java .nio .file .FileSystem ;
35+ import java .nio .file .FileSystems ;
36+ import java .nio .file .Files ;
37+ import java .nio .file .Path ;
3238import java .util .ArrayList ;
39+ import java .util .Collections ;
3340import java .util .HashMap ;
3441import java .util .HashSet ;
3542import java .util .IdentityHashMap ;
@@ -785,16 +792,30 @@ public static class Encoder extends ObjectCopier {
785792
786793 /**
787794 * Map from values to static final fields. In a serialized object graph, references to such
788- * values are encoded with a reference to the field.
795+ * values are encoded using the static final field they come from. This field is then looked
796+ * up via reflection when the value needs to be decoded.
789797 */
790- final Map <Object , Field > externalValues = new IdentityHashMap <>() ;
798+ final Map <Object , Field > externalValues ;
791799
792- public Encoder (List <Field > externalValues ) {
800+ public Encoder (List <Field > externalValueFields ) {
801+ this (gatherExternalValues (externalValueFields ));
802+ }
803+
804+ /**
805+ * Use precomputed {@code externalValues} to avoid recomputing them.
806+ */
807+ public Encoder (Map <Object , Field > externalValues ) {
793808 objects .add (null );
794809 objectToId .put (null , new ObjectID (0 , null ));
795- for (Field f : externalValues ) {
796- addExternalValue (f );
810+ this .externalValues = externalValues ;
811+ }
812+
813+ public static Map <Object , Field > gatherExternalValues (List <Field > externalValueFields ) {
814+ Map <Object , Field > result = new IdentityHashMap <>();
815+ for (Field f : externalValueFields ) {
816+ addExternalValue (result , f );
797817 }
818+ return result ;
798819 }
799820
800821 /**
@@ -808,7 +829,7 @@ protected ClassInfo makeClassInfo(Class<?> declaringClass) {
808829 return ClassInfo .of (declaringClass );
809830 }
810831
811- private void addExternalValue (Field field ) {
832+ private static void addExternalValue (Map < Object , Field > externalValues , Field field ) {
812833 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 );
813834 Object value = readField (field , null );
814835 if (value == null ) {
@@ -825,7 +846,7 @@ private void addExternalValue(Field field) {
825846 }
826847
827848 public Map <Object , Field > getExternalValues () {
828- return externalValues ;
849+ return Collections . unmodifiableMap ( externalValues ) ;
829850 }
830851
831852 private String encodeMap (UnmodifiableEconomicMap <?, ?> map ) {
@@ -1000,6 +1021,79 @@ public static Field getField(Class<?> declaredClass, String fieldName) {
10001021 }
10011022 }
10021023
1024+ public static List <Field > getExternalValueFields () throws IOException {
1025+ List <Field > externalValues = new ArrayList <>();
1026+ addImmutableCollectionsFields (externalValues );
1027+ addStaticFinalObjectFields (LocationIdentity .class , externalValues );
1028+
1029+ try (FileSystem fs = FileSystems .newFileSystem (URI .create ("jrt:/" ), Collections .emptyMap ())) {
1030+ for (String module : List .of ("jdk.internal.vm.ci" , "jdk.graal.compiler" , "com.oracle.graal.graal_enterprise" )) {
1031+ Path top = fs .getPath ("/modules/" + module );
1032+ try (Stream <Path > files = Files .find (top , Integer .MAX_VALUE , (path , attrs ) -> attrs .isRegularFile ())) {
1033+ files .forEach (p -> {
1034+ String fileName = p .getFileName ().toString ();
1035+ if (fileName .endsWith (".class" ) && !fileName .equals ("module-info.class" )) {
1036+ // Strip module prefix and convert to dotted form
1037+ int nameCount = p .getNameCount ();
1038+ String className = p .subpath (2 , nameCount ).toString ().replace ('/' , '.' );
1039+ // Strip ".class" suffix
1040+ className = className .replace ('/' , '.' ).substring (0 , className .length () - ".class" .length ());
1041+ try {
1042+ Class <?> graalClass = Class .forName (className );
1043+ addStaticFinalObjectFields (graalClass , externalValues );
1044+ } catch (ClassNotFoundException e ) {
1045+ throw new GraalError (e );
1046+ }
1047+ }
1048+ });
1049+ }
1050+ }
1051+ }
1052+ return externalValues ;
1053+ }
1054+
1055+ /**
1056+ * Adds the static, final, non-primitive fields of non-enum {@code declaringClass} to
1057+ * {@code fields}. In the process, the fields are made {@linkplain Field#setAccessible
1058+ * accessible}.
1059+ */
1060+ public static void addStaticFinalObjectFields (Class <?> declaringClass , List <Field > fields ) {
1061+ if (Enum .class .isAssignableFrom (declaringClass )) {
1062+ return ;
1063+ }
1064+ for (Field field : declaringClass .getDeclaredFields ()) {
1065+ int fieldModifiers = field .getModifiers ();
1066+ int fieldMask = Modifier .STATIC | Modifier .FINAL ;
1067+ if ((fieldModifiers & fieldMask ) != fieldMask ) {
1068+ continue ;
1069+ }
1070+ if (field .getType ().isPrimitive ()) {
1071+ continue ;
1072+ }
1073+ field .setAccessible (true );
1074+ fields .add (field );
1075+ }
1076+ }
1077+
1078+ /**
1079+ * Adds the EMPTY* fields from {@code java.util.ImmutableCollections} to {@code fields}, making
1080+ * them {@linkplain Field#setAccessible accessible} in the process.
1081+ */
1082+ private static void addImmutableCollectionsFields (List <Field > fields ) {
1083+ Class <?> c = List .of ().getClass ().getDeclaringClass ();
1084+ GraalError .guarantee (c .getName ().equals ("java.util.ImmutableCollections" ), "Incompatible ImmutableCollections class" );
1085+ for (Field f : c .getDeclaredFields ()) {
1086+ if (f .getName ().startsWith ("EMPTY" )) {
1087+ int modifiers = f .getModifiers ();
1088+ GraalError .guarantee (Modifier .isStatic (modifiers ), "Expect %s to be static" , f );
1089+ GraalError .guarantee (Modifier .isFinal (modifiers ), "Expect %s to be final" , f );
1090+ GraalError .guarantee (!f .getType ().isPrimitive (), "Expect %s to be non-primitive" , f );
1091+ f .setAccessible (true );
1092+ fields .add (f );
1093+ }
1094+ }
1095+ }
1096+
10031097 /**
10041098 * Describes the path from a root object to a target object. That is, the sequence of field and
10051099 * array reads performed on the root object to access the target object.
0 commit comments