< prev index next >

src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java

Print this page
*** 22,20 ***
--- 22,31 ---
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  package jdk.internal.classfile.impl.verifier;
  
+ import java.lang.classfile.ClassFile;
  import java.lang.classfile.ClassHierarchyResolver;
  import java.lang.classfile.ClassModel;
  import jdk.internal.classfile.components.ClassPrinter;
+ 
+ import java.lang.classfile.FieldModel;
+ import java.lang.classfile.constantpool.NameAndTypeEntry;
+ import java.lang.constant.ConstantDescs;
+ import java.lang.reflect.AccessFlag;
  import java.util.ArrayList;
  import java.util.Collections;
+ import java.util.HashSet;
  import java.util.List;
+ import java.util.Objects;
+ import java.util.Set;
  import java.util.function.Consumer;
  
  import jdk.internal.classfile.impl.ClassHierarchyImpl;
  import jdk.internal.classfile.impl.RawBytecodeHelper;
+ import jdk.internal.classfile.impl.TemporaryConstantPool;
+ import jdk.internal.classfile.impl.Util;
  import jdk.internal.classfile.impl.verifier.VerificationSignature.BasicType;
  import jdk.internal.classfile.impl.verifier.VerificationWrapper.ConstantPoolWrapper;
  
  import static jdk.internal.classfile.impl.RawBytecodeHelper.*;
  import static jdk.internal.classfile.impl.verifier.VerificationFrame.FLAG_THIS_UNINIT;

*** 103,10 ***
--- 114,11 ---
  
  
      static final int STACKMAP_ATTRIBUTE_MAJOR_VERSION = 50;
      static final int INVOKEDYNAMIC_MAJOR_VERSION = 51;
      static final int NOFAILOVER_MAJOR_VERSION = 51;
+     static final int VALUE_TYPES_MAJOR_VERSION = Util.VALUE_OBJECTS_MAJOR;
      static final int MAX_CODE_SIZE = 65535;
  
      public static List<VerifyError> verify(ClassModel classModel, Consumer<String> logger) {
          return verify(classModel, ClassHierarchyResolver.defaultResolver(), logger);
      }

*** 240,10 ***
--- 252,16 ---
  
      private VerificationType object_type() {
          return VerificationType.reference_type(java_lang_Object);
      }
  
+     static boolean supports_strict_fields(VerificationWrapper klass) {
+         int ver = klass.majorVersion();
+         return ver > VALUE_TYPES_MAJOR_VERSION ||
+                 (ver == VALUE_TYPES_MAJOR_VERSION && klass.clm.minorVersion() == ClassFile.PREVIEW_MINOR_VERSION);
+     }
+ 
      List<VerifyError> verify_class() {
          log_info("Verifying class %s with new format", _klass.thisClassName());
          var errors = new ArrayList<VerifyError>();
          for (VerificationWrapper.MethodWrapper m : _klass.methods()) {
              if (m.isNative() || m.isAbstract() || m.isBridge()) {

*** 301,11 ***
          int max_locals = m.maxLocals();
          int max_stack = m.maxStack();
          byte[] stackmap_data = m.stackMapTableRawData();
          var cp = m.constantPool();
          if (!VerificationSignature.isValidMethodSignature(m.descriptor())) verifyError("Invalid method signature");
!         VerificationFrame current_frame = new VerificationFrame(max_locals, max_stack, this);
          VerificationType return_type = current_frame.set_locals_from_arg(m, current_type());
          int stackmap_index = 0;
          int code_length = m.codeLength();
          if (code_length < 1 || code_length > MAX_CODE_SIZE) {
              verifyError(String.format("Invalid method Code length %d", code_length));
--- 319,23 ---
          int max_locals = m.maxLocals();
          int max_stack = m.maxStack();
          byte[] stackmap_data = m.stackMapTableRawData();
          var cp = m.constantPool();
          if (!VerificationSignature.isValidMethodSignature(m.descriptor())) verifyError("Invalid method signature");
! 
+         // Collect the initial strict instance fields
+         Set<NameAndTypeEntry> strict_fields = new HashSet<>();
+         if (m.name().equals(ConstantDescs.INIT_NAME)) {
+             for (var fs : current_class().clm.fields()) {
+                 if (fs.flags().has(AccessFlag.STRICT_INIT) && !fs.flags().has(AccessFlag.STATIC)) {
+                     var new_field = TemporaryConstantPool.INSTANCE.nameAndTypeEntry(fs.fieldName(), fs.fieldType());
+                     strict_fields.add(new_field);
+                 }
+             }
+         }
+ 
+         VerificationFrame current_frame = new VerificationFrame(max_locals, max_stack, strict_fields, this);
          VerificationType return_type = current_frame.set_locals_from_arg(m, current_type());
          int stackmap_index = 0;
          int code_length = m.codeLength();
          if (code_length < 1 || code_length > MAX_CODE_SIZE) {
              verifyError(String.format("Invalid method Code length %d", code_length));

*** 315,11 ***
          int ex_minmax[] = new int[] {code_length, -1};
          verify_exception_handler_table(code_length, code_data, ex_minmax);
          verify_local_variable_table(code_length, code_data);
  
          var reader = new VerificationTable.StackMapReader(stackmap_data, code_data, code_length, current_frame,
!                 (char) max_locals, (char) max_stack, cp, this);
          VerificationTable stackmap_table = new VerificationTable(reader, cp, this);
  
          var bcs = code.start();
          boolean no_control_flow = false;
          int opcode;
--- 345,11 ---
          int ex_minmax[] = new int[] {code_length, -1};
          verify_exception_handler_table(code_length, code_data, ex_minmax);
          verify_local_variable_table(code_length, code_data);
  
          var reader = new VerificationTable.StackMapReader(stackmap_data, code_data, code_length, current_frame,
!                 (char) max_locals, (char) max_stack, strict_fields, cp, this);
          VerificationTable stackmap_table = new VerificationTable(reader, cp, this);
  
          var bcs = code.start();
          boolean no_control_flow = false;
          int opcode;

*** 1045,17 ***
                          stackmap_table.check_jump_target(
                              current_frame, target);
                          no_control_flow = false; break;
                      case IF_ACMPEQ :
                      case IF_ACMPNE :
!                         current_frame.pop_stack(
-                             VerificationType.reference_check);
                          // fall through
                      case IFNULL :
                      case IFNONNULL :
!                         current_frame.pop_stack(
-                             VerificationType.reference_check);
                          target = bcs.dest();
                          stackmap_table.check_jump_target
                              (current_frame, target);
                          no_control_flow = false; break;
                      case GOTO :
--- 1075,15 ---
                          stackmap_table.check_jump_target(
                              current_frame, target);
                          no_control_flow = false; break;
                      case IF_ACMPEQ :
                      case IF_ACMPNE :
!                         current_frame.pop_stack(object_type());
                          // fall through
                      case IFNULL :
                      case IFNONNULL :
!                         current_frame.pop_stack(object_type());
                          target = bcs.dest();
                          stackmap_table.check_jump_target
                              (current_frame, target);
                          no_control_flow = false; break;
                      case GOTO :

*** 1497,14 ***
              case PUTFIELD ->  {
                  for (int i = n - 1; i >= 0; i--) {
                      current_frame.pop_stack(field_type[i]);
                  }
                  stack_object_type = current_frame.pop_stack();
!                 if (stack_object_type.is_uninitialized_this(this) &&
!                         target_class_type.equals(current_type()) &&
!                         _klass.findField(field_name, field_sig)) {
!                     stack_object_type = current_type();
                  }
                  is_assignable = target_class_type.is_assignable_from(stack_object_type, this);
                  if (!is_assignable) {
                      verifyError("Bad type on operand stack in putfield");
                  }
--- 1525,28 ---
              case PUTFIELD ->  {
                  for (int i = n - 1; i >= 0; i--) {
                      current_frame.pop_stack(field_type[i]);
                  }
                  stack_object_type = current_frame.pop_stack();
!                 FieldModel fd = _klass.findField(field_name, field_sig);
!                 boolean is_local_field = fd != null &&
!                         target_class_type.equals(current_type());
!                 if (stack_object_type.is_uninitialized_this(this)) {
+                     if (is_local_field) {
+                         // Set the type to the current type so the is_assignable check passes.
+                         stack_object_type = current_type();
+ 
+                         if (fd.flags().has(AccessFlag.STRICT_INIT)) {
+                             current_frame.satisfy_unset_field(fd.fieldName(), fd.fieldType());
+                         }
+                     }
+                 } else if (supports_strict_fields(_klass)) {
+                     // `strict` fields are not writable, but only local fields produce verification errors
+                     if (is_local_field && fd.flags().has(AccessFlag.STRICT_INIT) && fd.flags().has(AccessFlag.FINAL)) {
+                         verifyError("Bad code %d %s".formatted(bci,
+                                 "Illegal use of putfield on a strict field: %s:%s".formatted(fd.fieldName(), fd.fieldType())));
+                     }
                  }
                  is_assignable = target_class_type.is_assignable_from(stack_object_type, this);
                  if (!is_assignable) {
                      verifyError("Bad type on operand stack in putfield");
                  }

*** 1528,10 ***
--- 1570,15 ---
          if (type.is_uninitialized_this(this)) {
              String superk_name = current_class().superclassName();
              if (!current_class().thisClassName().equals(ref_class_type.name()) &&
                      !superk_name.equals(ref_class_type.name())) {
                  verifyError("Bad <init> method call");
+             } else if (ref_class_type.name().equals(superk_name)) {
+                 // Strict final fields must be satisfied by this point
+                 if (!current_frame.verify_unset_fields_satisfied()) {
+                     current_frame.unsatisfied_strict_fields_error(current_class(), bci);
+                 }
              }
              if (in_try_block) {
                  for(var exhandler : _method.exceptionTable()) {
                      int start_pc = exhandler[0];
                      int end_pc = exhandler[1];
< prev index next >