3636import java .util .Iterator ;
3737import java .util .LinkedHashMap ;
3838import java .util .List ;
39- import java .util .Locale ;
4039import java .util .Map ;
4140import java .util .Set ;
4241import java .util .function .BiConsumer ;
5655
5756import jdk .graal .compiler .core .common .FieldIntrospection ;
5857import jdk .graal .compiler .debug .GraalError ;
58+ import jdk .graal .compiler .replacements .SnippetTemplate ;
5959import jdk .internal .misc .Unsafe ;
6060
6161/**
@@ -92,7 +92,7 @@ public class ObjectCopier {
9292 /**
9393 * A builtin is specialized support for encoded and decoding values of specific types.
9494 */
95- abstract static class Builtin {
95+ public abstract static class Builtin {
9696 /**
9797 * The primary type for this builtin.
9898 */
@@ -148,15 +148,15 @@ void makeChildIds(Encoder encoder, Object obj, ObjectPath objectPath) {
148148 * Encodes the value of {@code obj} to a String that does not contain {@code '\n'} or
149149 * {@code '\r'}.
150150 */
151- abstract String encode (Encoder encoder , Object obj );
151+ protected abstract String encode (Encoder encoder , Object obj );
152152
153153 /**
154154 * Decodes {@code encoded} to an object of a type handled by this builtin.
155155 *
156156 * @param encoding the non-default encoded used when encoded the object or null if the
157157 * default encoded was used
158158 */
159- abstract Object decode (Decoder decoder , Class <?> concreteType , String encoding , String encoded );
159+ protected abstract Object decode (Decoder decoder , Class <?> concreteType , String encoding , String encoded );
160160
161161 @ Override
162162 public String toString () {
@@ -182,12 +182,12 @@ static final class ClassBuiltin extends Builtin {
182182 }
183183
184184 @ Override
185- String encode (Encoder encoder , Object obj ) {
185+ protected String encode (Encoder encoder , Object obj ) {
186186 return ((Class <?>) obj ).getName ();
187187 }
188188
189189 @ Override
190- Object decode (Decoder decoder , Class <?> concreteType , String encoding , String encoded ) {
190+ protected Object decode (Decoder decoder , Class <?> concreteType , String encoding , String encoded ) {
191191 return switch (encoded ) {
192192 case "boolean" -> boolean .class ;
193193 case "byte" -> byte .class ;
@@ -230,7 +230,7 @@ String encodingName(Object obj) {
230230 }
231231
232232 @ Override
233- String encode (Encoder encoder , Object obj ) {
233+ protected String encode (Encoder encoder , Object obj ) {
234234 String s = obj instanceof String ? (String ) obj : new String ((char []) obj );
235235 if ("escaped" .equals (encodingName (s ))) {
236236 return s .replace ("\\ " , "\\ \\ " ).replace ("\n " , "\\ n" ).replace ("\r " , "\\ r" );
@@ -239,7 +239,7 @@ String encode(Encoder encoder, Object obj) {
239239 }
240240
241241 @ Override
242- Object decode (Decoder decoder , Class <?> concreteType , String encoding , String encoded ) {
242+ protected Object decode (Decoder decoder , Class <?> concreteType , String encoding , String encoded ) {
243243 String s = encoded ;
244244 if (encoding != null ) {
245245 GraalError .guarantee (encoding .equals ("escaped" ), "Unknown encoded: %s" , encoding );
@@ -270,13 +270,13 @@ static final class EnumBuiltin extends Builtin {
270270 }
271271
272272 @ Override
273- String encode (Encoder encoder , Object obj ) {
273+ protected String encode (Encoder encoder , Object obj ) {
274274 return ((Enum <?>) obj ).name ();
275275 }
276276
277277 @ SuppressWarnings ({"unchecked" , "rawtypes" })
278278 @ Override
279- Object decode (Decoder decoder , Class <?> concreteType , String encoding , String encoded ) {
279+ protected Object decode (Decoder decoder , Class <?> concreteType , String encoding , String encoded ) {
280280 return Enum .valueOf ((Class ) concreteType , encoded );
281281 }
282282 }
@@ -297,11 +297,13 @@ static final class HashMapBuiltin extends Builtin {
297297 final Map <Class <?>, Supplier <?>> factories ;
298298
299299 HashMapBuiltin () {
300- super (HashMap .class , IdentityHashMap .class , LinkedHashMap .class );
300+ super (HashMap .class , IdentityHashMap .class , LinkedHashMap .class , SnippetTemplate .LRUCache .class );
301+ int size = SnippetTemplate .Options .MaxTemplatesPerSnippet .getDefaultValue ();
301302 factories = Map .of (
302303 HashMap .class , HashMap ::new ,
303304 IdentityHashMap .class , IdentityHashMap ::new ,
304- LinkedHashMap .class , LinkedHashMap ::new );
305+ LinkedHashMap .class , LinkedHashMap ::new ,
306+ SnippetTemplate .LRUCache .class , () -> new SnippetTemplate .LRUCache <>(size , size ));
305307 }
306308
307309 @ Override
@@ -311,14 +313,14 @@ void makeChildIds(Encoder encoder, Object obj, ObjectPath objectPath) {
311313 }
312314
313315 @ Override
314- String encode (Encoder encoder , Object obj ) {
316+ protected String encode (Encoder encoder , Object obj ) {
315317 Map <?, ?> map = (Map <?, ?>) obj ;
316318 return encoder .encodeMap (new EconomicMapWrap <>(map ));
317319 }
318320
319321 @ SuppressWarnings ("unchecked" )
320322 @ Override
321- Object decode (Decoder decoder , Class <?> concreteType , String encoding , String encoded ) {
323+ protected Object decode (Decoder decoder , Class <?> concreteType , String encoding , String encoded ) {
322324 Map <Object , Object > map = (Map <Object , Object >) factories .get (concreteType ).get ();
323325 decoder .decodeMap (encoded , map ::put );
324326 return map ;
@@ -350,12 +352,12 @@ void makeChildIds(Encoder encoder, Object obj, ObjectPath objectPath) {
350352 }
351353
352354 @ Override
353- String encode (Encoder encoder , Object obj ) {
355+ protected String encode (Encoder encoder , Object obj ) {
354356 return encoder .encodeMap ((UnmodifiableEconomicMap <?, ?>) obj );
355357 }
356358
357359 @ Override
358- Object decode (Decoder decoder , Class <?> concreteType , String encoding , String encoded ) {
360+ protected Object decode (Decoder decoder , Class <?> concreteType , String encoding , String encoded ) {
359361 if (EconomicMap .class .isAssignableFrom (concreteType )) {
360362 EconomicMap <Object , Object > map = EconomicMap .create ();
361363 decoder .decodeMap (encoded , map ::put );
@@ -374,8 +376,8 @@ Object decode(Decoder decoder, Class<?> concreteType, String encoding, String en
374376 * have the same name in which case the descriptor includes the qualified name of the
375377 * class declaring the field as a prefix.
376378 */
377- record ClassInfo (Class <?> clazz , Map <String , Field > fields ) {
378- static ClassInfo of (Class <?> declaringClass ) {
379+ public record ClassInfo (Class <?> clazz , Map <String , Field > fields ) {
380+ public static ClassInfo of (Class <?> declaringClass ) {
379381 Map <String , Field > fields = new HashMap <>();
380382 for (Class <?> c = declaringClass ; !c .equals (Object .class ); c = c .getSuperclass ()) {
381383 for (Field f : c .getDeclaredFields ()) {
@@ -387,9 +389,6 @@ static ClassInfo of(Class<?> declaringClass) {
387389 }
388390 Field conflict = fields .put (fieldDesc , f );
389391 GraalError .guarantee (conflict == null , "Cannot support 2 fields with same name and declaring class: %s and %s" , conflict , f );
390-
391- // Try to avoid problems with identity hash codes
392- GraalError .guarantee (!f .getName ().toLowerCase (Locale .ROOT ).contains ("hash" ), "Cannot serialize hash field: %s" , f );
393392 }
394393 }
395394 }
@@ -403,7 +402,7 @@ static ClassInfo of(Class<?> declaringClass) {
403402 final Map <Class <?>, Builtin > builtinClasses = new HashMap <>();
404403 final Set <Class <?>> notBuiltins = new HashSet <>();
405404
406- final void addBuiltin (Builtin builtin ) {
405+ protected final void addBuiltin (Builtin builtin ) {
407406 addBuiltin (builtin , builtin .clazz );
408407 }
409408
@@ -439,13 +438,9 @@ static String[] splitSpaceSeparatedElements(String elements) {
439438 }
440439
441440 /**
442- * Encodes {@code root} to a String.
443- *
444- * @param externalValues static fields whose values should not be encoded but instead
445- * represented as a reference to the field
441+ * Encodes {@code root} to a String using {@code encoder}.
446442 */
447- public static String encode (Object root , List <Field > externalValues ) {
448- Encoder encoder = new Encoder (externalValues );
443+ public static String encode (Encoder encoder , Object root ) {
449444 int rootId = encoder .makeId (root , ObjectPath .of ("[root]" )).id ();
450445 GraalError .guarantee (rootId == 1 , "The root object should have id of 1, not %d" , rootId );
451446 ByteArrayOutputStream baos = new ByteArrayOutputStream ();
@@ -457,15 +452,19 @@ public static String encode(Object root, List<Field> externalValues) {
457452
458453 public static Object decode (String encoded , ClassLoader loader ) {
459454 Decoder decoder = new Decoder (loader );
455+ return decode (decoder , encoded );
456+ }
457+
458+ public static Object decode (Decoder decoder , String encoded ) {
460459 return decoder .decode (encoded );
461460 }
462461
463- static class Decoder extends ObjectCopier {
462+ public static class Decoder extends ObjectCopier {
464463
465464 private final Map <Integer , Object > idToObject = new HashMap <>();
466465 private final ClassLoader loader ;
467466
468- Decoder (ClassLoader loader ) {
467+ public Decoder (ClassLoader loader ) {
469468 this .loader = loader ;
470469 }
471470
@@ -779,7 +778,7 @@ final Builtin getBuiltin(Class<?> clazz, boolean onlyCheck) {
779778 return b ;
780779 }
781780
782- static class Encoder extends ObjectCopier {
781+ public static class Encoder extends ObjectCopier {
783782
784783 final Map <Object , ObjectID > objectToId = new IdentityHashMap <>();
785784 final List <Object > objects = new ArrayList <>();
@@ -790,14 +789,25 @@ static class Encoder extends ObjectCopier {
790789 */
791790 final Map <Object , Field > externalValues = new IdentityHashMap <>();
792791
793- Encoder (List <Field > externalValues ) {
792+ public Encoder (List <Field > externalValues ) {
794793 objects .add (null );
795794 objectToId .put (null , new ObjectID (0 , null ));
796795 for (Field f : externalValues ) {
797796 addExternalValue (f );
798797 }
799798 }
800799
800+ /**
801+ * Gets a {@link ClassInfo} for encoding the fields of {@code declaringClass}.
802+ * <p>
803+ * A subclass can override this to enforce encoding invariants on classes or fields.
804+ *
805+ * @throws GraalError if an invariant is violated
806+ */
807+ protected ClassInfo makeClassInfo (Class <?> declaringClass ) {
808+ return ClassInfo .of (declaringClass );
809+ }
810+
801811 private void addExternalValue (Field field ) {
802812 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 );
803813 Object value = readField (field , null );
@@ -814,6 +824,10 @@ private void addExternalValue(Field field) {
814824
815825 }
816826
827+ public Map <Object , Field > getExternalValues () {
828+ return externalValues ;
829+ }
830+
817831 private String encodeMap (UnmodifiableEconomicMap <?, ?> map ) {
818832 UnmodifiableMapCursor <?, ?> cursor = map .getEntries ();
819833 StringBuilder value = new StringBuilder ();
@@ -858,8 +872,6 @@ ObjectID makeId(Object obj, ObjectPath objectPath) {
858872 objectToId .put (field , id );
859873 return id ;
860874 }
861- checkIllegalValue (Field .class , obj , objectPath , "Field type is used in object copying implementation" );
862- checkIllegalValue (FieldIntrospection .class , obj , objectPath , "Graal metadata type cannot be copied" );
863875
864876 objects .add (obj );
865877 objectToId .put (obj , id );
@@ -871,6 +883,10 @@ ObjectID makeId(Object obj, ObjectPath objectPath) {
871883 builtin .makeChildIds (this , obj , objectPath );
872884 return id ;
873885 }
886+
887+ checkIllegalValue (Field .class , obj , objectPath , "Field type is used in object copying implementation" );
888+ checkIllegalValue (FieldIntrospection .class , obj , objectPath , "Graal metadata type cannot be copied" );
889+
874890 if (clazz .isArray ()) {
875891 Class <?> componentType = clazz .getComponentType ();
876892 if (!componentType .isPrimitive ()) {
@@ -885,7 +901,7 @@ ObjectID makeId(Object obj, ObjectPath objectPath) {
885901 checkIllegalValue (LocationIdentity .class , obj , objectPath , "must come from a static field" );
886902 checkIllegalValue (HashSet .class , obj , objectPath , "hashes are typically not stable across VM executions" );
887903
888- ClassInfo classInfo = classInfos .computeIfAbsent (clazz , ClassInfo :: of );
904+ ClassInfo classInfo = classInfos .computeIfAbsent (clazz , this :: makeClassInfo );
889905 for (Field f : classInfo .fields ().values ()) {
890906 makeId (f .getType (), objectPath .add (f .getName () + ":type" ));
891907 if (!f .getType ().isPrimitive ()) {
0 commit comments