< prev index next >

src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java

Print this page
*** 1,7 ***
  /*
!  * 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
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this
--- 1,7 ---
  /*
!  * Copyright (c) 2022, 2025, 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
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this

*** 24,49 ***
   */
  
  package jdk.internal.classfile.impl;
  
  import java.lang.classfile.BufWriter;
  import java.lang.classfile.ClassReader;
  import java.lang.classfile.Label;
  import java.lang.classfile.MethodModel;
  import java.lang.classfile.attribute.StackMapFrameInfo;
  import java.lang.classfile.attribute.StackMapFrameInfo.ObjectVerificationTypeInfo;
  import java.lang.classfile.attribute.StackMapFrameInfo.SimpleVerificationTypeInfo;
  import java.lang.classfile.attribute.StackMapFrameInfo.UninitializedVerificationTypeInfo;
  import java.lang.classfile.attribute.StackMapFrameInfo.VerificationTypeInfo;
  import java.lang.classfile.constantpool.ClassEntry;
  import java.lang.constant.ConstantDescs;
  import java.lang.constant.MethodTypeDesc;
  import java.lang.reflect.AccessFlag;
  import java.util.Arrays;
  import java.util.Comparator;
  import java.util.List;
  import java.util.Objects;
  
  import static java.lang.classfile.ClassFile.ACC_STATIC;
  import static java.lang.classfile.attribute.StackMapFrameInfo.VerificationTypeInfo.*;
  import static java.util.Objects.requireNonNull;
  
  public class StackMapDecoder {
  
!     private static final int
                      SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247,
                      SAME_EXTENDED = 251;
      private static final StackMapFrameInfo[] NO_STACK_FRAME_INFOS = {};
  
      private final ClassReader classReader;
      private final int pos;
      private final LabelContext ctx;
      private final List<VerificationTypeInfo> initFrameLocals;
      private int p;
  
!     StackMapDecoder(ClassReader classReader, int pos, LabelContext ctx, List<VerificationTypeInfo> initFrameLocals) {
          this.classReader = classReader;
          this.pos = pos;
          this.ctx = ctx;
          this.initFrameLocals = initFrameLocals;
      }
  
      static List<VerificationTypeInfo> initFrameLocals(MethodModel method) {
          return initFrameLocals(method.parent().orElseThrow().thisClass(),
                  method.methodName().stringValue(),
--- 24,62 ---
   */
  
  package jdk.internal.classfile.impl;
  
  import java.lang.classfile.BufWriter;
+ import java.lang.classfile.ClassModel;
  import java.lang.classfile.ClassReader;
  import java.lang.classfile.Label;
  import java.lang.classfile.MethodModel;
  import java.lang.classfile.attribute.StackMapFrameInfo;
  import java.lang.classfile.attribute.StackMapFrameInfo.ObjectVerificationTypeInfo;
  import java.lang.classfile.attribute.StackMapFrameInfo.SimpleVerificationTypeInfo;
  import java.lang.classfile.attribute.StackMapFrameInfo.UninitializedVerificationTypeInfo;
  import java.lang.classfile.attribute.StackMapFrameInfo.VerificationTypeInfo;
  import java.lang.classfile.constantpool.ClassEntry;
+ import java.lang.classfile.constantpool.NameAndTypeEntry;
+ import java.lang.classfile.constantpool.PoolEntry;
+ import java.lang.classfile.constantpool.Utf8Entry;
  import java.lang.constant.ConstantDescs;
  import java.lang.constant.MethodTypeDesc;
  import java.lang.reflect.AccessFlag;
+ import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Comparator;
  import java.util.List;
  import java.util.Objects;
  
+ import jdk.internal.access.SharedSecrets;
+ 
  import static java.lang.classfile.ClassFile.ACC_STATIC;
+ import static java.lang.classfile.ClassFile.ACC_STRICT;
  import static java.lang.classfile.attribute.StackMapFrameInfo.VerificationTypeInfo.*;
  import static java.util.Objects.requireNonNull;
  
  public class StackMapDecoder {
  
!     static final int
+                     EARLY_LARVAL = 246,
                      SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247,
                      SAME_EXTENDED = 251;
+     private static final int BASE_FRAMES_UPPER_LIMIT = SAME_LOCALS_1_STACK_ITEM_EXTENDED; // not inclusive
      private static final StackMapFrameInfo[] NO_STACK_FRAME_INFOS = {};
  
      private final ClassReader classReader;
      private final int pos;
      private final LabelContext ctx;
      private final List<VerificationTypeInfo> initFrameLocals;
+     private final List<NameAndTypeEntry> initFrameUnsets;
      private int p;
  
!     StackMapDecoder(ClassReader classReader, int pos, LabelContext ctx, List<VerificationTypeInfo> initFrameLocals,
+                     List<NameAndTypeEntry> initFrameUnsets) {
          this.classReader = classReader;
          this.pos = pos;
          this.ctx = ctx;
          this.initFrameLocals = initFrameLocals;
+         this.initFrameUnsets = initFrameUnsets;
      }
  
      static List<VerificationTypeInfo> initFrameLocals(MethodModel method) {
          return initFrameLocals(method.parent().orElseThrow().thisClass(),
                  method.methodName().stringValue(),

*** 99,18 ***
--- 112,46 ---
              };
          }
          return List.of(vtis);
      }
  
+     static List<NameAndTypeEntry> initFrameUnsets(MethodModel method) {
+         return initFrameUnsets(method.parent().orElseThrow(),
+                 method.methodName());
+     }
+ 
+     private static List<NameAndTypeEntry> initFrameUnsets(ClassModel clazz, Utf8Entry methodName) {
+         if (!methodName.equalsString(ConstantDescs.INIT_NAME))
+             return List.of();
+         var l = new ArrayList<NameAndTypeEntry>(clazz.fields().size());
+         for (var field : clazz.fields()) {
+             if ((field.flags().flagsMask() & (ACC_STATIC | ACC_STRICT)) == ACC_STRICT) { // instance strict
+                 l.add(TemporaryConstantPool.INSTANCE.nameAndTypeEntry(field.fieldName(), field.fieldType()));
+             }
+         }
+         return List.copyOf(l);
+     }
+ 
+     private static List<NameAndTypeEntry> initFrameUnsets(MethodInfo mi, WritableField.UnsetField[] unsets) {
+         if (!mi.methodName().equalsString(ConstantDescs.INIT_NAME))
+             return List.of();
+         var l = new ArrayList<NameAndTypeEntry>(unsets.length);
+         for (var field : unsets) {
+             l.add(TemporaryConstantPool.INSTANCE.nameAndTypeEntry(field.name(), field.type()));
+         }
+         return List.copyOf(l);
+     }
+ 
      public static void writeFrames(BufWriter b, List<StackMapFrameInfo> entries) {
          var buf = (BufWriterImpl)b;
          var dcb = (DirectCodeBuilder)buf.labelContext();
          var mi = dcb.methodInfo();
          var prevLocals = StackMapDecoder.initFrameLocals(buf.thisClass(),
                  mi.methodName().stringValue(),
                  mi.methodTypeSymbol(),
                  (mi.methodFlags() & ACC_STATIC) != 0);
+         var prevUnsets = initFrameUnsets(mi, buf.getStrictInstanceFields());
          int prevOffset = -1;
          // avoid using method handles due to early bootstrap
          StackMapFrameInfo[] infos = entries.toArray(NO_STACK_FRAME_INFOS);
          //sort by resolved label offsets first to allow unordered entries
          Arrays.sort(infos, new Comparator<StackMapFrameInfo>() {

*** 122,18 ***
          for (var fr : infos) {
              int offset = dcb.labelToBci(fr.target());
              if (offset == prevOffset) {
                  throw new IllegalArgumentException("Duplicated stack frame bytecode index: " + offset);
              }
!             writeFrame(buf, offset - prevOffset - 1, prevLocals, fr);
              prevOffset = offset;
              prevLocals = fr.locals();
          }
      }
  
!     private static void writeFrame(BufWriterImpl out, int offsetDelta, List<VerificationTypeInfo> prevLocals, StackMapFrameInfo fr) {
          if (offsetDelta < 0) throw new IllegalArgumentException("Invalid stack map frames order");
          if (fr.stack().isEmpty()) {
              int commonLocalsSize = Math.min(prevLocals.size(), fr.locals().size());
              int diffLocalsSize = fr.locals().size() - prevLocals.size();
              if (-3 <= diffLocalsSize && diffLocalsSize <= 3 && equals(fr.locals(), prevLocals, commonLocalsSize)) {
                  if (diffLocalsSize == 0 && offsetDelta < 64) { //same frame
--- 163,36 ---
          for (var fr : infos) {
              int offset = dcb.labelToBci(fr.target());
              if (offset == prevOffset) {
                  throw new IllegalArgumentException("Duplicated stack frame bytecode index: " + offset);
              }
!             writeFrame(buf, offset - prevOffset - 1, prevLocals, prevUnsets, fr);
              prevOffset = offset;
              prevLocals = fr.locals();
+             prevUnsets = fr.unsetFields();
+         }
+     }
+ 
+     // In sync with StackMapGenerator::needsLarvalFrame
+     private static boolean needsLarvalFrameForTransition(List<NameAndTypeEntry> prevUnsets, StackMapFrameInfo fr) {
+         if (prevUnsets.equals(fr.unsetFields()))
+             return false;
+         if (!fr.locals().contains(SimpleVerificationTypeInfo.UNINITIALIZED_THIS)) {
+             assert fr.unsetFields().isEmpty() : fr; // should be checked in StackMapFrameInfo constructor
+             return false;
          }
+         return true;
      }
  
!     private static void writeFrame(BufWriterImpl out, int offsetDelta, List<VerificationTypeInfo> prevLocals, List<NameAndTypeEntry> prevUnsets, StackMapFrameInfo fr) {
          if (offsetDelta < 0) throw new IllegalArgumentException("Invalid stack map frames order");
+         // enclosing frames
+         if (needsLarvalFrameForTransition(prevUnsets, fr)) {
+             out.writeU1(EARLY_LARVAL);
+             Util.writeListIndices(out, fr.unsetFields());
+         }
+         // base frame
          if (fr.stack().isEmpty()) {
              int commonLocalsSize = Math.min(prevLocals.size(), fr.locals().size());
              int diffLocalsSize = fr.locals().size() - prevLocals.size();
              if (-3 <= diffLocalsSize && diffLocalsSize <= 3 && equals(fr.locals(), prevLocals, commonLocalsSize)) {
                  if (diffLocalsSize == 0 && offsetDelta < 64) { //same frame

*** 179,26 ***
                  bw.writeU1U2(tag, bw.labelContext().labelToBci(((UninitializedVerificationTypeInfo)vti).newTarget()));
              default -> throw new IllegalArgumentException("Invalid verification type tag: " + vti.tag());
          }
      }
  
      List<StackMapFrameInfo> entries() {
          p = pos;
          List<VerificationTypeInfo> locals = initFrameLocals, stack = List.of();
          int bci = -1;
          var entries = new StackMapFrameInfo[u2()];
          for (int ei = 0; ei < entries.length; ei++) {
!             int frameType = classReader.readU1(p++);
              if (frameType < 64) {
                  bci += frameType + 1;
                  stack = List.of();
              } else if (frameType < 128) {
                  bci += frameType - 63;
                  stack = List.of(readVerificationTypeInfo());
              } else {
!                 if (frameType < SAME_LOCALS_1_STACK_ITEM_EXTENDED)
!                     throw new IllegalArgumentException("Invalid stackmap frame type: " + frameType);
                  bci += u2() + 1;
                  if (frameType == SAME_LOCALS_1_STACK_ITEM_EXTENDED) {
                      stack = List.of(readVerificationTypeInfo());
                  } else if (frameType < SAME_EXTENDED) {
                      locals = locals.subList(0, locals.size() + frameType - SAME_EXTENDED);
--- 238,47 ---
                  bw.writeU1U2(tag, bw.labelContext().labelToBci(((UninitializedVerificationTypeInfo)vti).newTarget()));
              default -> throw new IllegalArgumentException("Invalid verification type tag: " + vti.tag());
          }
      }
  
+     // Copied from BoundAttribute
+     <E extends PoolEntry> List<E> readEntryList(int p, Class<E> type) {
+         int cnt = classReader.readU2(p);
+         p += 2;
+         var entries = new Object[cnt];
+         int end = p + (cnt * 2);
+         for (int i = 0; p < end; i++, p += 2) {
+             entries[i] = classReader.readEntry(p, type);
+         }
+         return SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(entries);
+     }
+ 
      List<StackMapFrameInfo> entries() {
          p = pos;
          List<VerificationTypeInfo> locals = initFrameLocals, stack = List.of();
+         List<NameAndTypeEntry> unsetFields = initFrameUnsets;
          int bci = -1;
          var entries = new StackMapFrameInfo[u2()];
          for (int ei = 0; ei < entries.length; ei++) {
!             int actualFrameType = classReader.readU1(p++);
+             int frameType = actualFrameType; // effective frame type for parsing
+             // enclosing frames handling
+             if (frameType == EARLY_LARVAL) {
+                 unsetFields = readEntryList(p, NameAndTypeEntry.class);
+                 p += 2 + unsetFields.size() * 2;
+                 frameType = classReader.readU1(p++);
+             }
+             // base frame handling
              if (frameType < 64) {
                  bci += frameType + 1;
                  stack = List.of();
              } else if (frameType < 128) {
                  bci += frameType - 63;
                  stack = List.of(readVerificationTypeInfo());
              } else {
!                 if (frameType < BASE_FRAMES_UPPER_LIMIT)
!                     throw new IllegalArgumentException("Invalid base frame type: " + frameType);
                  bci += u2() + 1;
                  if (frameType == SAME_LOCALS_1_STACK_ITEM_EXTENDED) {
                      stack = List.of(readVerificationTypeInfo());
                  } else if (frameType < SAME_EXTENDED) {
                      locals = locals.subList(0, locals.size() + frameType - SAME_EXTENDED);

*** 221,14 ***
                          newStack[i] = readVerificationTypeInfo();
                      locals = List.of(newLocals);
                      stack = List.of(newStack);
                  }
              }
!             entries[ei] = new StackMapFrameImpl(frameType,
!                         ctx.getLabel(bci),
!                         locals,
!                         stack);
          }
          return List.of(entries);
      }
  
      private VerificationTypeInfo readVerificationTypeInfo() {
--- 301,19 ---
                          newStack[i] = readVerificationTypeInfo();
                      locals = List.of(newLocals);
                      stack = List.of(newStack);
                  }
              }
!             if (actualFrameType != EARLY_LARVAL && !unsetFields.isEmpty() && !locals.contains(SimpleVerificationTypeInfo.UNINITIALIZED_THIS)) {
!                 // clear unsets post larval
!                 unsetFields = List.of();
!             }
+             entries[ei] = new StackMapFrameImpl(actualFrameType,
+                     ctx.getLabel(bci),
+                     locals,
+                     stack,
+                     unsetFields);
          }
          return List.of(entries);
      }
  
      private VerificationTypeInfo readVerificationTypeInfo() {

*** 297,14 ***
      }
  
      public static record StackMapFrameImpl(int frameType,
                                             Label target,
                                             List<VerificationTypeInfo> locals,
!                                            List<VerificationTypeInfo> stack)
              implements StackMapFrameInfo {
          public StackMapFrameImpl {
              requireNonNull(target);
              locals = List.copyOf(locals);
              stack = List.copyOf(stack);
          }
      }
  }
--- 382,33 ---
      }
  
      public static record StackMapFrameImpl(int frameType,
                                             Label target,
                                             List<VerificationTypeInfo> locals,
!                                            List<VerificationTypeInfo> stack,
+                                            List<NameAndTypeEntry> unsetFields)
              implements StackMapFrameInfo {
          public StackMapFrameImpl {
              requireNonNull(target);
              locals = List.copyOf(locals);
              stack = List.copyOf(stack);
+             unsetFields = List.copyOf(unsetFields);
+ 
+             uninitializedThisCheck:
+             if (!unsetFields.isEmpty()) {
+                 for (var local : locals) {
+                     if (local == SimpleVerificationTypeInfo.UNINITIALIZED_THIS) {
+                         break uninitializedThisCheck;
+                     }
+                 }
+                 throw new IllegalArgumentException("unset fields requires uninitializedThis in locals");
+             }
+         }
+ 
+         public StackMapFrameImpl(int frameType,
+                                  Label target,
+                                  List<VerificationTypeInfo> locals,
+                                  List<VerificationTypeInfo> stack) {
+             this(frameType, target, locals, stack, List.of());
          }
      }
  }
< prev index next >