1 /*
  2  * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 import java.io.PrintStream;
 25 import java.util.ArrayList;
 26 import java.util.Collections;
 27 import java.util.HashMap;
 28 import java.util.List;
 29 
 30 import javax.management.RuntimeErrorException;
 31 
 32 import jdk.test.lib.Asserts;
 33 import jdk.test.lib.process.OutputAnalyzer;
 34 import jdk.test.lib.process.ProcessTools;
 35 
 36 public class FieldLayoutAnalyzer {
 37 
 38   // Mutable wrapper around log output to manage the cursor while parsing
 39   static class LogOutput {
 40     List<String> lines;
 41     int cursor;
 42 
 43     public LogOutput(List<String> lines) {
 44       this.lines = lines;
 45       cursor = 0;
 46     }
 47 
 48     String getCurrentLine() { return lines.get(cursor); }
 49     String get(int idx) { return lines.get(idx); }
 50     int size() { return lines.size(); }
 51     void moveToNextLine() { cursor = cursor + 1; }
 52     boolean hasMoreLines() { return cursor < lines.size(); }
 53   }
 54 
 55   static enum BlockType {
 56     RESERVED,
 57     INHERITED,
 58     EMPTY,
 59     REGULAR,
 60     PADDING,
 61     FLAT,
 62     NULL_MARKER;
 63 
 64     static BlockType parseType(String s) {
 65       switch(s) {
 66         case "RESERVED"    : return RESERVED;
 67         case "INHERITED"   : return INHERITED;
 68         case "EMPTY"       : return EMPTY;
 69         case "REGULAR"     : return REGULAR;
 70         case "PADDING"     : return PADDING;
 71         case "FLAT"        : return FLAT;
 72         case "NULL_MARKER" : return NULL_MARKER;
 73         default:
 74           throw new RuntimeException("Unknown block type: " + s);
 75       }
 76     }
 77   }
 78 
 79   static enum LayoutKind {
 80     NON_FLAT,
 81     NON_ATOMIC_FLAT,
 82     ATOMIC_FLAT,
 83     NULLABLE_FLAT;
 84 
 85     static LayoutKind parseLayoutKind(String s) {
 86       switch(s) {
 87         case ""                : return NON_FLAT;
 88         case "NON_ATOMIC_FLAT" : return NON_ATOMIC_FLAT;
 89         case "ATOMIC_FLAT"     : return ATOMIC_FLAT;
 90         case "NULLABLE_ATOMIC_FLAT"   : return NULLABLE_FLAT;
 91         default:
 92           throw new RuntimeException("Unknown layout kind: " + s);
 93       }
 94     }
 95   }
 96 
 97   static public record FieldBlock (int offset,
 98                             BlockType type,
 99                             int size,
100                             int alignment,
101                             String name,
102                             String signature,
103                             String fieldClass,
104                             LayoutKind layoutKind) {
105 
106     static FieldBlock createSpecialBlock(int offset, BlockType type, int size, int alignment) {
107       return new FieldBlock(offset, type, size, alignment, null, null, null, LayoutKind.NON_FLAT);
108     }
109 
110     static FieldBlock createJavaFieldBlock(int offset, BlockType type, int size, int alignment, String name, String signature, String fieldClass, LayoutKind layoutKind) {
111       return new FieldBlock(offset, type, size, alignment, name, signature, fieldClass, layoutKind);
112     }
113 
114     void print(PrintStream out) {
115       out.println("Offset=" + offset+
116                 " type=" + type +
117                 " size=" + size +
118                 " alignment=" + alignment +
119                 " name=" + name +
120                 " signature=" + signature +
121                 " fieldClass=" + fieldClass);
122     }
123 
124     boolean isFlat() { return type == BlockType.FLAT; } // Warning: always return false for inherited fields, even flat ones
125 
126     static FieldBlock parseField(String line) {
127       String[] fieldLine = line.split("\\s+");
128       // for(String  s : fieldLine) {
129       //   System.out.print("["+s+"]");  // debugging statement to be removed
130       // }
131       // System.out.println();
132       int offset = Integer.parseInt(fieldLine[1].substring(1, fieldLine[1].length()));
133       BlockType type = BlockType.parseType(fieldLine[2]);
134       String[] size_align = fieldLine[3].split("/");
135       int size = Integer.parseInt(size_align[0]);
136       int alignment = -1;
137       if (type != BlockType.RESERVED) {
138         alignment = Integer.parseInt(size_align[1]);
139       } else {
140         Asserts.assertTrue(size_align[1].equals("-"));
141       }
142       FieldBlock block = null;
143       switch(type) {
144         case BlockType.RESERVED:
145         case BlockType.EMPTY:
146         case BlockType.PADDING: {
147             block = FieldBlock.createSpecialBlock(offset, type, size, alignment);
148             break;
149         }
150         case BlockType.REGULAR:
151         case BlockType.INHERITED:
152         case BlockType.FLAT: {
153             String name = fieldLine[4];
154             String signature = fieldLine[5];
155             String fieldClass = "";
156             String layoutKind = "";
157             int nullMarkerOffset = -1;
158             if (type == BlockType.FLAT) {
159               fieldClass = fieldLine[6];
160               layoutKind = fieldLine[7];
161             }
162             block = FieldBlock.createJavaFieldBlock(offset, type, size, alignment, name, signature, fieldClass, LayoutKind.parseLayoutKind(layoutKind));
163             break;
164           }
165         case BlockType.NULL_MARKER: {
166           block = FieldBlock.createSpecialBlock(offset, type, size, alignment);
167           break;
168         }
169       }
170       Asserts.assertNotNull(block);
171       return block;
172     }
173 
174   }
175 
176   static class ClassLayout {
177     String name;
178     String superName;
179     boolean isValue;
180     int instanceSize;
181     int payloadSize;
182     int payloadAlignment;
183     int firstFieldOffset;
184     int nonAtomicLayoutSize;         // -1 if no non-nullable layout
185     int nonAtomicLayoutAlignment;    // -1 if no non-nullable layout
186     int atomicLayoutSize;            // -1 if no atomic layout
187     int atomicLayoutAlignment;       // -1 if no atomic layout
188     int nullableLayoutSize;          // -1 if no nullable layout
189     int nullableLayoutAlignment;     // -1 if no nullable layout
190     int nullMarkerOffset;            // -1 if no nullable layout
191     String[] lines;
192     ArrayList<FieldBlock> staticFields;
193     ArrayList<FieldBlock> nonStaticFields;
194 
195     private ClassLayout() {
196       staticFields = new ArrayList<FieldBlock>();
197       nonStaticFields = new ArrayList<FieldBlock>();
198     }
199 
200     boolean hasNonAtomicLayout() { return nonAtomicLayoutSize != -1; }
201     boolean hasAtomicLayout() { return atomicLayoutSize != -1; }
202     boolean hasNullableLayout() { return nullableLayoutSize != -1; }
203     boolean hasNullMarker() {return nullMarkerOffset != -1; }
204 
205     int getSize(LayoutKind layoutKind) {
206       switch(layoutKind) {
207         case NON_FLAT:
208           throw new RuntimeException("Should not be called on non-flat fields");
209         case NON_ATOMIC_FLAT:
210           Asserts.assertTrue(nonAtomicLayoutSize != -1);
211           return nonAtomicLayoutSize;
212         case ATOMIC_FLAT:
213           Asserts.assertTrue(atomicLayoutSize != -1);
214           return atomicLayoutSize;
215         case NULLABLE_FLAT:
216           Asserts.assertTrue(nullableLayoutSize != -1);
217           return nullableLayoutSize;
218         default:
219           throw new RuntimeException("Unknown LayoutKind " + layoutKind);
220       }
221     }
222 
223     int getAlignment(LayoutKind layoutKind) {
224       switch(layoutKind) {
225         case NON_FLAT:
226           throw new RuntimeException("Should not be called on non-flat fields");
227         case NON_ATOMIC_FLAT:
228           Asserts.assertTrue(nonAtomicLayoutSize != -1);
229           return nonAtomicLayoutAlignment;
230         case ATOMIC_FLAT:
231           Asserts.assertTrue(atomicLayoutSize != -1);
232           return atomicLayoutAlignment;
233         case NULLABLE_FLAT:
234           Asserts.assertTrue(nullableLayoutSize != -1);
235           return nullableLayoutAlignment;
236         default:
237           throw new RuntimeException("Unknown LayoutKind " + layoutKind);
238       }
239     }
240 
241     void processField(String line, boolean isStatic) {
242       FieldBlock block = FieldBlock.parseField(line);
243       if (isStatic) {
244         Asserts.assertTrue(block.type != BlockType.INHERITED); // static fields cannotbe inherited
245         staticFields.add(block);
246       } else {
247         nonStaticFields.add(block);
248       }
249     }
250 
251     static ClassLayout parseClassLayout(LogOutput lo) {
252       ClassLayout cl = new ClassLayout();
253       // Parsing class name
254       Asserts.assertTrue(lo.getCurrentLine().startsWith("Layout of class"), lo.getCurrentLine());
255       String[] first = lo.getCurrentLine().split("\\s+");
256       cl.name = first[3];
257       if (first.length == 6) {
258         Asserts.assertEquals(first[4], "extends");
259         cl.superName = first[5];
260       } else {
261         cl.superName = null;
262       }
263       // System.out.println("Class name: " + cl.name);
264       lo.moveToNextLine();
265       Asserts.assertTrue(lo.getCurrentLine().startsWith("Instance fields:"), lo.getCurrentLine());
266       lo.moveToNextLine();
267       // Parsing instance fields
268       while (lo.getCurrentLine().startsWith(" @")) {
269         cl.processField(lo.getCurrentLine(), false);
270         lo.moveToNextLine();
271       }
272       Asserts.assertTrue(lo.getCurrentLine().startsWith("Static fields:"), lo.getCurrentLine());
273       lo.moveToNextLine();
274       // Parsing static fields
275       while (lo.getCurrentLine().startsWith(" @")) {
276         cl.processField(lo.getCurrentLine(), true);
277         lo.moveToNextLine();
278       }
279       Asserts.assertTrue(lo.getCurrentLine().startsWith("Instance size ="), lo.getCurrentLine());
280       String[] sizeLine = lo.getCurrentLine().split("\\s+");
281       cl.instanceSize = Integer.parseInt(sizeLine[3]);
282       lo.moveToNextLine();
283       if (lo.getCurrentLine().startsWith("First field offset =")) {
284         // The class is a value class, more lines to parse
285         cl.isValue = true;
286         // First field offset = xx
287         String[] firstOffsetLine = lo.getCurrentLine().split("\\s+");
288         cl.firstFieldOffset = Integer.parseInt(firstOffsetLine[4]);
289         lo.moveToNextLine();
290         // Payload layout: x/y
291         Asserts.assertTrue(lo.getCurrentLine().startsWith("Payload layout"));
292         String[] payloadLayoutLine = lo.getCurrentLine().split("\\s+");
293         String[] size_align = payloadLayoutLine[2].split("/");
294         cl.payloadSize = Integer.parseInt(size_align[0]);
295         cl.payloadAlignment = Integer.parseInt(size_align[1]);
296         lo.moveToNextLine();
297         // Non atomic flat layout: x/y
298         Asserts.assertTrue(lo.getCurrentLine().startsWith("Non atomic flat layout"));
299         String[] nonAtomicLayoutLine = lo.getCurrentLine().split("\\s+");
300         size_align = nonAtomicLayoutLine[4].split("/");
301         if (size_align[0].contentEquals("-")) {
302           Asserts.assertTrue(size_align[1].contentEquals("-"), "Size/Alignment mismatch");
303           cl.nonAtomicLayoutSize = -1;
304           cl.nonAtomicLayoutAlignment = -1;
305         } else {
306           cl.nonAtomicLayoutSize = Integer.parseInt(size_align[0]);
307           cl.nonAtomicLayoutAlignment = Integer.parseInt(size_align[1]);
308         }
309         lo.moveToNextLine();
310         // Atomic flat layout: x/y
311         Asserts.assertTrue(lo.getCurrentLine().startsWith("Atomic flat layout"));
312         String[] atomicLayoutLine = lo.getCurrentLine().split("\\s+");
313         size_align = atomicLayoutLine[3].split("/");
314         if (size_align[0].contentEquals("-")) {
315           Asserts.assertTrue(size_align[1].contentEquals("-"), "Size/Alignment mismatch");
316           cl.atomicLayoutSize = -1;
317           cl.atomicLayoutAlignment = -1;
318         } else {
319           cl.atomicLayoutSize = Integer.parseInt(size_align[0]);
320           cl.atomicLayoutAlignment = Integer.parseInt(size_align[1]);
321         }
322         lo.moveToNextLine();
323         // Nullable flat layout: x/y
324         Asserts.assertTrue(lo.getCurrentLine().startsWith("Nullable flat layout"));
325         String[] nullableLayoutLine = lo.getCurrentLine().split("\\s+");
326         size_align = nullableLayoutLine[3].split("/");
327         if (size_align[0].contentEquals("-")) {
328           Asserts.assertTrue(size_align[1].contentEquals("-"), "Size/Alignment mismatch");
329           cl.nullableLayoutSize = -1;
330           cl.nullableLayoutAlignment = -1;
331         } else {
332           cl.nullableLayoutSize = Integer.parseInt(size_align[0]);
333           cl.nullableLayoutAlignment = Integer.parseInt(size_align[1]);
334         }
335         lo.moveToNextLine();
336         // Null marker offset = 15 (if class has a nullable flat layout)
337         if (cl.nullableLayoutSize != -1) {
338           Asserts.assertTrue(lo.getCurrentLine().startsWith("Null marker offset"));
339           String[] nullMarkerLine = lo.getCurrentLine().split("\\s+");
340           cl.nullMarkerOffset = Integer.parseInt(nullMarkerLine[4]);
341           lo.moveToNextLine();
342         } else {
343           cl.nullMarkerOffset = -1;
344         }
345       } else {
346         cl.isValue = false;
347       }
348 
349       Asserts.assertTrue(lo.getCurrentLine().startsWith("---"), lo.getCurrentLine());
350       lo.moveToNextLine();
351       return cl;
352     }
353 
354     FieldBlock getFieldAtOffset(int offset, boolean isStatic) {
355       ArrayList<FieldBlock> fields = isStatic ? staticFields : nonStaticFields;
356       for (FieldBlock block : fields) {
357         if (block.offset == offset) return block;
358       }
359       throw new RuntimeException("No " + (isStatic ? "static" : "nonstatic") + " field found at offset "+ offset);
360     }
361 
362     FieldBlock getFieldFromName(String fieldName, boolean isStatic) {
363       FieldBlock block = getFieldFromNameOrNull(fieldName, isStatic);
364       if (block == null) {
365         throw new RuntimeException("No " + (isStatic ? "static" : "nonstatic") + " field found with name "+ fieldName);
366       }
367       return block;
368     }
369 
370     FieldBlock getFieldFromNameOrNull(String fieldName, boolean isStatic) {
371       Asserts.assertTrue(fieldName != null);
372       ArrayList<FieldBlock> fields = isStatic ? staticFields : nonStaticFields;
373       for (FieldBlock block : fields) {
374         if (block.name() == null) continue;
375         String n = block.name().substring(1, block.name().length() - 1); // in the log, name is surrounded by double quotes
376         if (fieldName.equals(n)) return block;
377       }
378       return null;
379     }
380 
381   }
382 
383   ArrayList<ClassLayout> layouts;
384   int oopSize;
385 
386   static String signatureToName(String sig) {
387     Asserts.assertTrue((sig.charAt(0) == 'L'));
388     Asserts.assertTrue((sig.charAt(sig.length() - 1) == ';'));
389     return sig.substring(1, sig.length() - 1);
390   }
391 
392   private FieldLayoutAnalyzer() {
393     layouts = new ArrayList<ClassLayout>();
394   }
395 
396   public static FieldLayoutAnalyzer createFieldLayoutAnalyzer(LogOutput lo) {
397     FieldLayoutAnalyzer fla = new FieldLayoutAnalyzer();
398     fla.generate(lo);
399     return fla;
400   }
401 
402   ClassLayout getClassLayout(String name) {
403     for(ClassLayout layout : layouts) {
404       if (layout.name.equals(name)) return layout;
405     }
406     return null;
407   }
408 
409   ClassLayout getClassLayoutFromName(String name) {
410     for(ClassLayout layout : layouts) {
411       String sub = layout.name.substring(0, layout.name.indexOf('@'));
412       if (name.equals(sub)) return layout;
413     }
414     return null;
415   }
416 
417   void checkOffsetOnFields(ArrayList<FieldBlock> fields) {
418     HashMap<Integer, FieldBlock> map = new HashMap<Integer, FieldBlock>();
419     for (FieldBlock fb : fields) {
420       Asserts.assertFalse(map.containsKey(fb.offset()), "Duplicate offset at " + fb.offset());
421       map.put(fb.offset(), fb);
422     }
423   }
424 
425   void checkOffsets() {
426     for (ClassLayout layout : layouts) {
427       try {
428         checkOffsetOnFields(layout.staticFields);
429         checkOffsetOnFields(layout.nonStaticFields);
430       } catch(Throwable t) {
431         System.out.println("Unexpection exception when checking offsets in class " + layout.name);
432         throw t;
433       }
434     }
435   }
436 
437   void checkNoOverlapOnFields(ArrayList<FieldBlock> fields) {
438     for (int i = 0; i < fields.size() - 1; i++) {
439       FieldBlock f0 = fields.get(i);
440       FieldBlock f1 = fields.get(i + 1);
441       if (f0.offset + f0.size < f1.offset) {
442         throw new RuntimeException("Hole issue found at offset " + f1.offset);
443       } else if (f0.offset + f0.size > f1.offset) {
444         throw new RuntimeException("Overlap issue found at offset " + f1.offset);
445       }
446     }
447   }
448 
449   void checkNoOverlap() {
450     for (ClassLayout layout : layouts) {
451       try {
452         checkNoOverlapOnFields(layout.staticFields);
453         checkNoOverlapOnFields(layout.nonStaticFields);
454       } catch(Throwable t) {
455         System.out.println("Unexpection exception when checking for overlaps/holes in class " + layout.name);
456         throw t;
457       }
458     }
459   }
460 
461   void checkSizeAndAlignmentForField(FieldBlock block) {
462     Asserts.assertTrue(block.size() > 0);
463     if (block.type == BlockType.RESERVED) {
464       Asserts.assertTrue(block.alignment == -1);
465       return;
466     }
467     if (block.type == BlockType.EMPTY || block.type == BlockType.PADDING
468         || block.type == BlockType.NULL_MARKER) {
469       Asserts.assertTrue(block.alignment == 1, "alignment = " + block.alignment);
470       return;
471     }
472 
473     switch(block.signature()) {
474       case "Z" :
475       case "B" : Asserts.assertTrue(block.size() == 1);
476                  Asserts.assertTrue(block.alignment() == 1);
477         break;
478       case "S" :
479       case "C" : Asserts.assertTrue(block.size() == 2);
480                  Asserts.assertTrue(block.alignment() == 2);
481         break;
482       case "F" :
483       case "I" : Asserts.assertTrue(block.size() == 4);
484                  Asserts.assertTrue(block.alignment() == 4);
485         break;
486       case "J" :
487       case "D" : Asserts.assertTrue(block.size() == 8);
488                  Asserts.assertTrue(block.alignment() == 8);
489         break;
490       default: {
491         if (block.signature().startsWith("[")) {
492           Asserts.assertEquals(oopSize, block.size());
493         } else if (block.signature().startsWith("L")) {
494           if (block.type == BlockType.INHERITED) {
495             // Skip for now, will be verified when checking the class declaring the field
496           } else if (block.type == BlockType.REGULAR) {
497             Asserts.assertEquals(oopSize, block.size());
498           } else {
499             Asserts.assertEquals(BlockType.FLAT, block.type);
500             ClassLayout fcl = getClassLayout(block.fieldClass);
501             Asserts.assertNotNull(fcl);
502             Asserts.assertEquals(block.size(), fcl.getSize(block.layoutKind));
503             Asserts.assertEquals(block.alignment(), fcl.getAlignment(block.layoutKind));
504           }
505         } else {
506           throw new RuntimeException("Unknown signature type: " + block.signature);
507         }
508       }
509       Asserts.assertTrue(block.offset % block.alignment == 0);
510     }
511   }
512 
513   void checkSizeAndAlignment() {
514     for (ClassLayout layout : layouts) {
515       try {
516         for (FieldBlock block : layout.staticFields) {
517           checkSizeAndAlignmentForField(block);
518         }
519       } catch(Throwable t) {
520         System.out.println("Unexpected exception when checking size and alignment in static fields of class " + layout.name);
521         throw t;
522       }
523       try {
524         for (FieldBlock block : layout.nonStaticFields) {
525           checkSizeAndAlignmentForField(block);
526         }
527       } catch(Throwable t) {
528         System.out.println("Unexpected exception when checking size and alignment in non-static fields of class " + layout.name);
529         throw t;
530       }
531     }
532   }
533 
534   // Verify that fields marked as INHERITED are declared in a super class
535   void checkInheritedFields() {
536     for (ClassLayout layout : layouts) {
537       try {
538         // Preparing the list of ClassLayout of super classes
539         ArrayList<ClassLayout> supers = new ArrayList<ClassLayout>();
540         String className = layout.superName;
541         while (className != null) {
542           ClassLayout cl = getClassLayout(className);
543           supers.add(cl);
544           className = cl.superName;
545         }
546         for (FieldBlock field : layout.nonStaticFields) {
547           if (field.type == BlockType.INHERITED) {
548             int i = 0;
549             boolean found = false;
550             FieldBlock b = null;
551             while(i < supers.size() && !found) {
552               b = supers.get(i).getFieldAtOffset(field.offset, false);
553               if (b.type != BlockType.INHERITED) found = true;
554               i++;
555             }
556             String location = new String(" at " + layout.name + " offset " + field.offset());
557             Asserts.assertTrue(found, "No declaration found for an inherited field " + location);
558             Asserts.assertNotEquals(field.type, BlockType.EMPTY, location);
559             Asserts.assertEquals(field.size, b.size, location);
560             Asserts.assertEquals(field.alignment, b.alignment, location );
561             Asserts.assertEquals(field.name(), b.name(), location);
562             Asserts.assertEquals(field.signature(), b.signature(), location);
563           }
564         }
565       } catch(Throwable t) {
566         System.out.println("Unexpexted exception when checking inherited fields in class " + layout.name);
567       }
568     }
569   }
570 
571   static class Node {
572     ClassLayout classLayout;
573     Node superClass;
574     ArrayList<Node> subClasses = new ArrayList<Node>();
575   }
576 
577   // Verify that all fields declared in a class are present in all subclass
578   void checkSubClasses() {
579     // Generating the class inheritance graph
580     HashMap<String, Node> nodes = new HashMap<String, Node>();
581     for (ClassLayout layout : layouts) {
582       try {
583         if (layout.name.contains("$$Lambda@0")) continue; // Skipping lambda classes
584         Node current = nodes.get(layout.name);
585         if (current == null) {
586           current = new Node();
587           nodes.put(layout.name, current);
588         }
589         if (current.classLayout == null) {
590           current.classLayout = layout;
591         } else {
592           Asserts.assertEQ(current.classLayout, layout);
593         }
594         if (layout.superName != null) {
595           Node superNode = nodes.get(layout.superName);
596           if (superNode == null) {
597             superNode = new Node();
598             superNode.subClasses.add(current);
599             nodes.put(layout.superName, superNode);
600           }
601           superNode.subClasses.add(current);
602         }
603       } catch(Throwable t) {
604         System.out.println("Unexpected exception when generating list of sub-classes of class " + layout.name);
605         throw t;
606       }
607     }
608     // Field verification
609     for (Node node : nodes.values()) {
610       ClassLayout layout = node.classLayout;
611       for (FieldBlock block : layout.nonStaticFields) {
612         if (block.offset() == 0) continue; // Skip object header
613         if (block.type() == BlockType.EMPTY) continue; // Empty spaces can be used by subclasses
614         if (block.type() == BlockType.PADDING) continue; // PADDING should have a finer inspection, preserved for @Contended and other imperative padding, and relaxed for abstract value conservative padding
615         if (block.type() == BlockType.NULL_MARKER) continue;
616         // A special case for PADDING might be needed too => must NOT be used in subclasses
617         for (Node subnode : node.subClasses) {
618           try {
619             checkFieldInClass(block, subnode);
620           } catch(Throwable t) {
621             System.out.println("Unexpected exception when checking subclass " + subnode.classLayout.name + " of class " + layout.name);
622             throw t;
623           }
624         }
625       }
626     }
627   }
628 
629   void checkFieldInClass(FieldBlock block, Node node) {
630     FieldBlock b = node.classLayout.getFieldAtOffset(block.offset, false);
631     Asserts.assertTrue(b.type == BlockType.INHERITED);
632     Asserts.assertEquals(b.signature(), block.signature());
633     Asserts.assertEquals(b.name(), block.name());
634     Asserts.assertEquals(b.size(), block.size());
635     Asserts.assertTrue(b.alignment() == block.alignment());
636     for (Node subnode : node.subClasses) {
637       checkFieldInClass(block, subnode);
638     }
639   }
640 
641   void checkNullMarkers() {
642     for (ClassLayout layout : layouts) {
643       try {
644         BlockType last_type = BlockType.RESERVED;
645         boolean has_empty_slot = false;
646         for (FieldBlock block : layout.nonStaticFields) {
647           last_type = block.type;
648           if (block.type() == BlockType.NULL_MARKER) {
649             Asserts.assertTrue(layout.hasNullMarker());
650             Asserts.assertTrue(layout.hasNullableLayout());
651             Asserts.assertEQ(block.offset(), layout.nullMarkerOffset);
652           }
653           if (block.type() == BlockType.EMPTY) has_empty_slot = true;
654         }
655         // null marker should not be added at the end of the layout if there's an empty slot
656         Asserts.assertTrue(last_type != BlockType.NULL_MARKER || has_empty_slot == false,
657                           "Problem detected in layout of class " + layout.name);
658         // static layout => must not have NULL_MARKERS because static fields are never flat
659         for (FieldBlock block : layout.staticFields) {
660           Asserts.assertNotEquals(block.type(), BlockType.NULL_MARKER);
661           Asserts.assertNotEquals(block.type(), BlockType.FLAT);
662         }
663       } catch(Throwable t) {
664         System.out.println("Unexpected exception while checking null markers in class " + layout.name);
665         throw t;
666       }
667     }
668   }
669 
670   void check() {
671     checkOffsets();
672     checkNoOverlap();
673     checkSizeAndAlignment();
674     checkInheritedFields();
675     checkSubClasses();
676     checkNullMarkers();
677   }
678 
679   private void generate(LogOutput lo) {
680     try {
681       while (lo.hasMoreLines()) {
682         if (lo.getCurrentLine().startsWith("Heap oop size = ")) {
683           String[] oopSizeLine = lo.getCurrentLine().split("\\s+");
684           oopSize = Integer.parseInt(oopSizeLine[4]);
685           Asserts.assertTrue(oopSize == 4 || oopSize == 8);
686         }
687         if (lo.getCurrentLine().startsWith("Layout of class")) {
688           ClassLayout cl = ClassLayout.parseClassLayout(lo);
689           layouts.add(cl);
690         } else {
691           lo.moveToNextLine(); // skipping line
692         }
693       }
694       Asserts.assertTrue(oopSize != 0);
695     } catch (Throwable t) {
696       System.out.println("Error while processing line: " + lo.getCurrentLine());
697       throw t;
698     }
699   }
700  }