< prev index next >

src/hotspot/share/interpreter/linkResolver.cpp

Print this page
*** 959,20 ***
                                   const LinkInfo& link_info,
                                   Bytecodes::Code byte, bool initialize_class,
                                   TRAPS) {
    assert(byte == Bytecodes::_getstatic || byte == Bytecodes::_putstatic ||
           byte == Bytecodes::_getfield  || byte == Bytecodes::_putfield  ||
           byte == Bytecodes::_nofast_getfield  || byte == Bytecodes::_nofast_putfield  ||
           (byte == Bytecodes::_nop && !link_info.check_access()), "bad field access bytecode");
  
    bool is_static = (byte == Bytecodes::_getstatic || byte == Bytecodes::_putstatic);
!   bool is_put    = (byte == Bytecodes::_putfield  || byte == Bytecodes::_putstatic || byte == Bytecodes::_nofast_putfield);
    // Check if there's a resolved klass containing the field
    Klass* resolved_klass = link_info.resolved_klass();
    Symbol* field = link_info.name();
    Symbol* sig = link_info.signature();
  
    // Resolve instance field
    Klass* sel_klass = resolved_klass->find_field(field, sig, &fd);
    // check if field exists; i.e., if a klass containing the field def has been selected
    if (sel_klass == nullptr) {
      ResourceMark rm(THREAD);
--- 959,37 ---
                                   const LinkInfo& link_info,
                                   Bytecodes::Code byte, bool initialize_class,
                                   TRAPS) {
    assert(byte == Bytecodes::_getstatic || byte == Bytecodes::_putstatic ||
           byte == Bytecodes::_getfield  || byte == Bytecodes::_putfield  ||
+          byte == Bytecodes::_withfield ||
           byte == Bytecodes::_nofast_getfield  || byte == Bytecodes::_nofast_putfield  ||
           (byte == Bytecodes::_nop && !link_info.check_access()), "bad field access bytecode");
  
    bool is_static = (byte == Bytecodes::_getstatic || byte == Bytecodes::_putstatic);
!   bool is_put    = (byte == Bytecodes::_putfield  || byte == Bytecodes::_putstatic ||
+                     byte == Bytecodes::_nofast_putfield || byte == Bytecodes::_withfield);
    // Check if there's a resolved klass containing the field
    Klass* resolved_klass = link_info.resolved_klass();
    Symbol* field = link_info.name();
    Symbol* sig = link_info.signature();
  
+ 
+   if (byte == Bytecodes::_withfield && !resolved_klass->is_inline_klass()) {
+     ResourceMark rm(THREAD);
+     char msg[200];
+     jio_snprintf(msg, sizeof(msg), "Bytecode withfield cannot be used on identity class %s", resolved_klass->external_name());
+     THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), msg);
+   }
+ 
+   if (is_put && !is_static && byte != Bytecodes::_withfield && resolved_klass->is_inline_klass()) {
+     ResourceMark rm(THREAD);
+     char msg[200];
+     jio_snprintf(msg, sizeof(msg), "Bytecode putfield cannot be used on primitive class %s", resolved_klass->external_name());
+     THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), msg);
+   }
+ 
    // Resolve instance field
    Klass* sel_klass = resolved_klass->find_field(field, sig, &fd);
    // check if field exists; i.e., if a klass containing the field def has been selected
    if (sel_klass == nullptr) {
      ResourceMark rm(THREAD);

*** 1000,30 ***
  
      // A final field can be modified only
      // (1) by methods declared in the class declaring the field and
      // (2) by the <clinit> method (in case of a static field)
      //     or by the <init> method (in case of an instance field).
      if (is_put && fd.access_flags().is_final()) {
  
        if (sel_klass != current_klass) {
!         ResourceMark rm(THREAD);
!         stringStream ss;
!         ss.print("Update to %s final field %s.%s attempted from a different class (%s) than the field's declaring class",
!                  is_static ? "static" : "non-static", resolved_klass->external_name(), fd.name()->as_C_string(),
!                 current_klass->external_name());
!         THROW_MSG(vmSymbols::java_lang_IllegalAccessError(), ss.as_string());
        }
  
        if (fd.constants()->pool_holder()->major_version() >= 53) {
          Method* m = link_info.current_method();
          assert(m != nullptr, "information about the current method must be available for 'put' bytecodes");
          bool is_initialized_static_final_update = (byte == Bytecodes::_putstatic &&
                                                     fd.is_static() &&
!                                                    !m->is_static_initializer());
          bool is_initialized_instance_final_update = ((byte == Bytecodes::_putfield || byte == Bytecodes::_nofast_putfield) &&
                                                       !fd.is_static() &&
!                                                      !m->is_object_initializer());
  
          if (is_initialized_static_final_update || is_initialized_instance_final_update) {
            ResourceMark rm(THREAD);
            stringStream ss;
            ss.print("Update to %s final field %s.%s attempted from a different method (%s) than the initializer method %s ",
--- 1017,41 ---
  
      // A final field can be modified only
      // (1) by methods declared in the class declaring the field and
      // (2) by the <clinit> method (in case of a static field)
      //     or by the <init> method (in case of an instance field).
+     // (3) by withfield when field is in a value type and the
+     //     selected class and current class are nest mates.
      if (is_put && fd.access_flags().is_final()) {
  
        if (sel_klass != current_klass) {
!         // If byte code is a withfield check if they are nestmates.
!         bool are_nestmates = false;
!         if (sel_klass->is_instance_klass() &&
!             InstanceKlass::cast(sel_klass)->is_inline_klass() &&
!             current_klass->is_instance_klass()) {
!           are_nestmates = InstanceKlass::cast(current_klass)->has_nestmate_access_to(InstanceKlass::cast(sel_klass), THREAD);
+         }
+         if (!are_nestmates) {
+           ResourceMark rm(THREAD);
+           stringStream ss;
+           ss.print("Update to %s final field %s.%s attempted from a different class (%s) than the field's declaring class",
+                    is_static ? "static" : "non-static", resolved_klass->external_name(), fd.name()->as_C_string(),
+                     current_klass->external_name());
+           THROW_MSG(vmSymbols::java_lang_IllegalAccessError(), ss.as_string());
+         }
        }
  
        if (fd.constants()->pool_holder()->major_version() >= 53) {
          Method* m = link_info.current_method();
          assert(m != nullptr, "information about the current method must be available for 'put' bytecodes");
          bool is_initialized_static_final_update = (byte == Bytecodes::_putstatic &&
                                                     fd.is_static() &&
!                                                    !m->is_class_initializer());
          bool is_initialized_instance_final_update = ((byte == Bytecodes::_putfield || byte == Bytecodes::_nofast_putfield) &&
                                                       !fd.is_static() &&
!                                                      !m->is_object_constructor());
  
          if (is_initialized_static_final_update || is_initialized_instance_final_update) {
            ResourceMark rm(THREAD);
            stringStream ss;
            ss.print("Update to %s final field %s.%s attempted from a different method (%s) than the initializer method %s ",

*** 1147,10 ***
--- 1175,12 ---
    } else {
      resolved_method = resolve_interface_method(link_info, Bytecodes::_invokespecial, CHECK_NULL);
    }
  
    // check if method name is <init>, that it is found in same klass as static type
+   // Since this method is never inherited from a super, any appearance here under
+   // the wrong class would be an error.
    if (resolved_method->name() == vmSymbols::object_initializer_name() &&
        resolved_method->method_holder() != resolved_klass) {
      ResourceMark rm(THREAD);
      stringStream ss;
      ss.print("%s: method '", resolved_klass->external_name());

*** 1218,11 ***
    // Invokespecial for a superinterface, resolved method is selected method,
    // no checks for shadowing
    methodHandle sel_method(THREAD, resolved_method());
  
    if (link_info.check_access() &&
!       // check if the method is not <init>
        resolved_method->name() != vmSymbols::object_initializer_name()) {
  
      Klass* current_klass = link_info.current_klass();
  
      // Check if the class of the resolved_klass is a superclass
--- 1248,11 ---
    // Invokespecial for a superinterface, resolved method is selected method,
    // no checks for shadowing
    methodHandle sel_method(THREAD, resolved_method());
  
    if (link_info.check_access() &&
!       // check if the method is not <init>, which is never inherited
        resolved_method->name() != vmSymbols::object_initializer_name()) {
  
      Klass* current_klass = link_info.current_klass();
  
      // Check if the class of the resolved_klass is a superclass

*** 1640,24 ***
    }
    return;
  }
  
  void LinkResolver::resolve_invoke(CallInfo& result, Handle& recv,
!                              const methodHandle& attached_method,
!                              Bytecodes::Code byte, TRAPS) {
    Klass* defc = attached_method->method_holder();
    Symbol* name = attached_method->name();
    Symbol* type = attached_method->signature();
    LinkInfo link_info(defc, name, type);
    switch(byte) {
      case Bytecodes::_invokevirtual:
!       resolve_virtual_call(result, recv, recv->klass(), link_info,
!                            /*check_null_and_abstract=*/true, CHECK);
        break;
      case Bytecodes::_invokeinterface:
!       resolve_interface_call(result, recv, recv->klass(), link_info,
!                              /*check_null_and_abstract=*/true, CHECK);
        break;
      case Bytecodes::_invokestatic:
        resolve_static_call(result, link_info, /*initialize_class=*/false, CHECK);
        break;
      case Bytecodes::_invokespecial:
--- 1670,25 ---
    }
    return;
  }
  
  void LinkResolver::resolve_invoke(CallInfo& result, Handle& recv,
!                                   const methodHandle& attached_method,
!                                   Bytecodes::Code byte, bool check_null_and_abstract, TRAPS) {
    Klass* defc = attached_method->method_holder();
    Symbol* name = attached_method->name();
    Symbol* type = attached_method->signature();
    LinkInfo link_info(defc, name, type);
+   Klass* recv_klass = recv.is_null() ? defc : recv->klass();
    switch(byte) {
      case Bytecodes::_invokevirtual:
!       resolve_virtual_call(result, recv, recv_klass, link_info,
!                            check_null_and_abstract, CHECK);
        break;
      case Bytecodes::_invokeinterface:
!       resolve_interface_call(result, recv, recv_klass, link_info,
!                              check_null_and_abstract, CHECK);
        break;
      case Bytecodes::_invokestatic:
        resolve_static_call(result, link_info, /*initialize_class=*/false, CHECK);
        break;
      case Bytecodes::_invokespecial:
< prev index next >