< prev index next >

src/hotspot/share/interpreter/interpreterRuntime.cpp

Print this page
@@ -23,10 +23,11 @@
   */
  
  #include "precompiled.hpp"
  #include "classfile/javaClasses.inline.hpp"
  #include "classfile/symbolTable.hpp"
+ #include "classfile/systemDictionary.hpp"
  #include "classfile/vmClasses.hpp"
  #include "classfile/vmSymbols.hpp"
  #include "code/codeCache.hpp"
  #include "compiler/compilationPolicy.hpp"
  #include "compiler/compileBroker.hpp"

@@ -43,10 +44,13 @@
  #include "memory/oopFactory.hpp"
  #include "memory/resourceArea.hpp"
  #include "memory/universe.hpp"
  #include "oops/constantPool.inline.hpp"
  #include "oops/cpCache.inline.hpp"
+ #include "oops/flatArrayKlass.hpp"
+ #include "oops/flatArrayOop.inline.hpp"
+ #include "oops/inlineKlass.inline.hpp"
  #include "oops/instanceKlass.inline.hpp"
  #include "oops/klass.inline.hpp"
  #include "oops/methodData.hpp"
  #include "oops/method.inline.hpp"
  #include "oops/objArrayKlass.hpp"

@@ -75,10 +79,11 @@
  #include "runtime/threadCritical.hpp"
  #include "utilities/align.hpp"
  #include "utilities/checkedCast.hpp"
  #include "utilities/copy.hpp"
  #include "utilities/events.hpp"
+ #include "utilities/globalDefinitions.hpp"
  #ifdef COMPILER2
  #include "opto/runtime.hpp"
  #endif
  
  // Helper class to access current interpreter state

@@ -156,11 +161,13 @@
    int cp_index = wide ? last_frame.get_index_u2(Bytecodes::_ldc_w) : last_frame.get_index_u1(Bytecodes::_ldc);
    constantTag tag = pool->tag_at(cp_index);
  
    assert (tag.is_unresolved_klass() || tag.is_klass(), "wrong ldc call");
    Klass* klass = pool->klass_at(cp_index, CHECK);
-   oop java_class = klass->java_mirror();
+   oop java_class = tag.is_Qdescriptor_klass()
+                       ? InlineKlass::cast(klass)->val_mirror()
+                       : klass->java_mirror();
    current->set_vm_result(java_class);
  JRT_END
  
  JRT_ENTRY(void, InterpreterRuntime::resolve_ldc(JavaThread* current, Bytecodes::Code bytecode)) {
    assert(bytecode == Bytecodes::_ldc ||

@@ -220,10 +227,14 @@
  
  JRT_ENTRY(void, InterpreterRuntime::_new(JavaThread* current, ConstantPool* pool, int index))
    Klass* k = pool->klass_at(index, CHECK);
    InstanceKlass* klass = InstanceKlass::cast(k);
  
+   if (klass->is_inline_klass()) {
+     THROW(vmSymbols::java_lang_InstantiationError());
+   }
+ 
    // Make sure we are not instantiating an abstract klass
    klass->check_valid_for_instantiation(true, CHECK);
  
    // Make sure klass is initialized
    klass->initialize(CHECK);

@@ -244,34 +255,254 @@
    //       because the _breakpoint bytecode would be lost.
    oop obj = klass->allocate_instance(CHECK);
    current->set_vm_result(obj);
  JRT_END
  
+ JRT_ENTRY(void, InterpreterRuntime::aconst_init(JavaThread* current, ConstantPool* pool, int index))
+   // Getting the InlineKlass
+   Klass* k = pool->klass_at(index, CHECK);
+   if (!k->is_inline_klass()) {
+     // inconsistency with 'new' which throws an InstantiationError
+     // in the future, aconst_init will just return null instead of throwing an exception
+     THROW(vmSymbols::java_lang_IncompatibleClassChangeError());
+   }
+   assert(k->is_inline_klass(), "aconst_init argument must be the inline type class");
+   InlineKlass* vklass = InlineKlass::cast(k);
+ 
+   vklass->initialize(CHECK);
+   oop res = vklass->default_value();
+   current->set_vm_result(res);
+ JRT_END
+ 
+ JRT_ENTRY(int, InterpreterRuntime::withfield(JavaThread* current, ResolvedFieldEntry* entry, uintptr_t ptr))
+   assert(entry->is_valid(), "Invalid ResolvedFieldEntry");
+   oop obj = nullptr;
+   int recv_offset = type2size[as_BasicType((TosState)entry->tos_state())];
+   assert(frame::interpreter_frame_expression_stack_direction() == -1, "currently is -1 on all platforms");
+   int ret_adj = (recv_offset + type2size[T_OBJECT] )* AbstractInterpreter::stackElementSize;
+   int offset = entry->field_offset();
+   obj = (oopDesc*)(((uintptr_t*)ptr)[recv_offset * Interpreter::stackElementWords]);
+   if (obj == nullptr) {
+     THROW_(vmSymbols::java_lang_NullPointerException(), ret_adj);
+   }
+   assert(oopDesc::is_oop(obj), "Verifying receiver");
+   assert(obj->klass()->is_inline_klass(), "Must have been checked during resolution");
+   instanceHandle old_value_h(THREAD, (instanceOop)obj);
+   oop ref = nullptr;
+   if (entry->tos_state() == atos) {
+     ref = *(oopDesc**)ptr;
+   }
+   Handle ref_h(THREAD, ref);
+   InlineKlass* ik = InlineKlass::cast(old_value_h()->klass());
+   // Ensure that the class is initialized or being initialized
+   // If the class is in error state, the creation of a new value should not be allowed
+   ik->initialize(CHECK_(ret_adj));
+ 
+   bool can_skip = false;
+   switch(entry->tos_state()) {
+     case ztos:
+       if (old_value_h()->bool_field(offset) == (jboolean)(*(jint*)ptr)) can_skip = true;
+       break;
+     case btos:
+       if (old_value_h()->byte_field(offset) == (jbyte)(*(jint*)ptr)) can_skip = true;
+       break;
+     case ctos:
+       if (old_value_h()->char_field(offset) == (jchar)(*(jint*)ptr)) can_skip = true;
+       break;
+     case stos:
+       if (old_value_h()->short_field(offset) == (jshort)(*(jint*)ptr)) can_skip = true;
+       break;
+     case itos:
+       if (old_value_h()->int_field(offset) == *(jint*)ptr) can_skip = true;
+       break;
+     case ltos:
+       if (old_value_h()->long_field(offset) == *(jlong*)ptr) can_skip = true;
+       break;
+     case ftos:
+       if (memcmp(old_value_h()->field_addr<jfloat>(offset), (jfloat*)ptr, sizeof(jfloat)) == 0) can_skip = true;
+       break;
+     case dtos:
+       if (memcmp(old_value_h()->field_addr<jdouble>(offset), (jdouble*)ptr, sizeof(jdouble)) == 0) can_skip = true;
+       break;
+     case atos:
+       if (!entry->is_flat() && old_value_h()->obj_field(offset) == ref_h()) can_skip = true;
+       break;
+     default:
+       break;
+   }
+   if (can_skip) {
+     current->set_vm_result(old_value_h());
+     return ret_adj;
+   }
+ 
+   instanceOop new_value = ik->allocate_instance_buffer(CHECK_(ret_adj));
+   Handle new_value_h = Handle(THREAD, new_value);
+   ik->inline_copy_oop_to_new_oop(old_value_h(), new_value_h());
+   switch(entry->tos_state()) {
+     case ztos:
+       new_value_h()->bool_field_put(offset, (jboolean)(*(jint*)ptr));
+       break;
+     case btos:
+       new_value_h()->byte_field_put(offset, (jbyte)(*(jint*)ptr));
+       break;
+     case ctos:
+       new_value_h()->char_field_put(offset, (jchar)(*(jint*)ptr));
+       break;
+     case stos:
+       new_value_h()->short_field_put(offset, (jshort)(*(jint*)ptr));
+       break;
+     case itos:
+       new_value_h()->int_field_put(offset, (*(jint*)ptr));
+       break;
+     case ltos:
+       new_value_h()->long_field_put(offset, *(jlong*)ptr);
+       break;
+     case ftos:
+       new_value_h()->float_field_put(offset, *(jfloat*)ptr);
+       break;
+     case dtos:
+       new_value_h()->double_field_put(offset, *(jdouble*)ptr);
+       break;
+     case atos:
+       {
+         if (entry->is_null_free_inline_type())  {
+           if (!entry->is_flat()) {
+             if (ref_h() == nullptr) {
+               THROW_(vmSymbols::java_lang_NullPointerException(), ret_adj);
+             }
+             new_value_h()->obj_field_put(offset, ref_h());
+           } else {
+             int field_index = entry->field_index();
+             InlineKlass* field_ik = InlineKlass::cast(ik->get_inline_type_field_klass(field_index));
+             field_ik->write_flat_field(new_value_h(), offset, ref_h(), CHECK_(ret_adj));
+           }
+         } else {
+           new_value_h()->obj_field_put(offset, ref_h());
+         }
+       }
+       break;
+     default:
+       ShouldNotReachHere();
+   }
+   current->set_vm_result(new_value_h());
+   return ret_adj;
+ JRT_END
+ 
+ JRT_ENTRY(void, InterpreterRuntime::uninitialized_static_inline_type_field(JavaThread* current, oopDesc* mirror, ResolvedFieldEntry* entry))
+   // The interpreter tries to access an inline static field that has not been initialized.
+   // This situation can happen in different scenarios:
+   //   1 - if the load or initialization of the field failed during step 8 of
+   //       the initialization of the holder of the field, in this case the access to the field
+   //       must fail
+   //   2 - it can also happen when the initialization of the holder class triggered the initialization of
+   //       another class which accesses this field in its static initializer, in this case the
+   //       access must succeed to allow circularity
+   // The code below tries to load and initialize the field's class again before returning the default value.
+   // If the field was not initialized because of an error, an exception should be thrown.
+   // If the class is being initialized, the default value is returned.
+   assert(entry->is_valid(), "Invalid ResolvedFieldEntry");
+   instanceHandle mirror_h(THREAD, (instanceOop)mirror);
+   InstanceKlass* klass = entry->field_holder();
+   u2 index = entry->field_index();
+   assert(klass == java_lang_Class::as_Klass(mirror), "Not the field holder klass");
+   assert(klass->field_is_null_free_inline_type(index), "Sanity check");
+   if (klass->is_being_initialized() && klass->is_init_thread(THREAD)) {
+     int offset = klass->field_offset(index);
+     Klass* field_k = klass->get_inline_type_field_klass_or_null(index);
+     if (field_k == nullptr) {
+       field_k = SystemDictionary::resolve_or_fail(klass->field_signature(index)->fundamental_name(THREAD),
+           Handle(THREAD, klass->class_loader()),
+           Handle(THREAD, klass->protection_domain()),
+           true, CHECK);
+       assert(field_k != nullptr, "Should have been loaded or an exception thrown above");
+       klass->set_inline_type_field_klass(index, field_k);
+     }
+     field_k->initialize(CHECK);
+     oop defaultvalue = InlineKlass::cast(field_k)->default_value();
+     // It is safe to initialize the static field because 1) the current thread is the initializing thread
+     // and is the only one that can access it, and 2) the field is actually not initialized (i.e. null)
+     // otherwise the JVM should not be executing this code.
+     mirror_h()->obj_field_put(offset, defaultvalue);
+     current->set_vm_result(defaultvalue);
+   } else {
+     assert(klass->is_in_error_state(), "If not initializing, initialization must have failed to get there");
+     ResourceMark rm(THREAD);
+     const char* desc = "Could not initialize class ";
+     const char* className = klass->external_name();
+     size_t msglen = strlen(desc) + strlen(className) + 1;
+     char* message = NEW_RESOURCE_ARRAY(char, msglen);
+     if (nullptr == message) {
+       // Out of memory: can't create detailed error message
+       THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), className);
+     } else {
+       jio_snprintf(message, msglen, "%s%s", desc, className);
+       THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), message);
+     }
+   }
+ JRT_END
+ 
+ JRT_ENTRY(void, InterpreterRuntime::read_flat_field(JavaThread* current, oopDesc* obj, int index, Klass* field_holder))
+   Handle obj_h(THREAD, obj);
+ 
+   assert(oopDesc::is_oop(obj), "Sanity check");
+ 
+   assert(field_holder->is_instance_klass(), "Sanity check");
+   InstanceKlass* klass = InstanceKlass::cast(field_holder);
+ 
+   assert(klass->field_is_flat(index), "Sanity check");
+ 
+   InlineKlass* field_vklass = InlineKlass::cast(klass->get_inline_type_field_klass(index));
+ 
+   oop res = field_vklass->read_flat_field(obj_h(), klass->field_offset(index), CHECK);
+   current->set_vm_result(res);
+ JRT_END
  
  JRT_ENTRY(void, InterpreterRuntime::newarray(JavaThread* current, BasicType type, jint size))
    oop obj = oopFactory::new_typeArray(type, size, CHECK);
    current->set_vm_result(obj);
  JRT_END
  
  
  JRT_ENTRY(void, InterpreterRuntime::anewarray(JavaThread* current, ConstantPool* pool, int index, jint size))
    Klass*    klass = pool->klass_at(index, CHECK);
-   objArrayOop obj = oopFactory::new_objArray(klass, size, CHECK);
+   bool      is_qtype_desc = pool->tag_at(index).is_Qdescriptor_klass();
+   arrayOop obj;
+   if ((!klass->is_array_klass()) && is_qtype_desc) { // Logically creates elements, ensure klass init
+     klass->initialize(CHECK);
+     obj = oopFactory::new_valueArray(klass, size, CHECK);
+   } else {
+     obj = oopFactory::new_objArray(klass, size, CHECK);
+   }
    current->set_vm_result(obj);
  JRT_END
  
+ JRT_ENTRY(void, InterpreterRuntime::value_array_load(JavaThread* current, arrayOopDesc* array, int index))
+   flatArrayHandle vah(current, (flatArrayOop)array);
+   oop value_holder = flatArrayOopDesc::value_alloc_copy_from_index(vah, index, CHECK);
+   current->set_vm_result(value_holder);
+ JRT_END
+ 
+ JRT_ENTRY(void, InterpreterRuntime::value_array_store(JavaThread* current, void* val, arrayOopDesc* array, int index))
+   assert(val != nullptr, "can't store null into flat array");
+   ((flatArrayOop)array)->value_copy_to_index(cast_to_oop(val), index);
+ JRT_END
  
  JRT_ENTRY(void, InterpreterRuntime::multianewarray(JavaThread* current, jint* first_size_address))
    // We may want to pass in more arguments - could make this slightly faster
    LastFrameAccessor last_frame(current);
    ConstantPool* constants = last_frame.method()->constants();
-   int          i = last_frame.get_index_u2(Bytecodes::_multianewarray);
-   Klass* klass   = constants->klass_at(i, CHECK);
+   int i = last_frame.get_index_u2(Bytecodes::_multianewarray);
+   Klass* klass = constants->klass_at(i, CHECK);
+   bool is_qtype = klass->name()->is_Q_array_signature();
    int   nof_dims = last_frame.number_of_dimensions();
    assert(klass->is_klass(), "not a class");
    assert(nof_dims >= 1, "multianewarray rank must be nonzero");
  
+   if (is_qtype) { // Logically creates elements, ensure klass init
+     klass->initialize(CHECK);
+   }
+ 
    // We must create an array of jints to pass to multi_allocate.
    ResourceMark rm(current);
    const int small_dims = 10;
    jint dim_array[small_dims];
    jint *dims = &dim_array[0];

@@ -292,10 +523,33 @@
    assert(oopDesc::is_oop(obj), "must be a valid oop");
    assert(obj->klass()->has_finalizer(), "shouldn't be here otherwise");
    InstanceKlass::register_finalizer(instanceOop(obj), CHECK);
  JRT_END
  
+ JRT_ENTRY(jboolean, InterpreterRuntime::is_substitutable(JavaThread* current, oopDesc* aobj, oopDesc* bobj))
+   assert(oopDesc::is_oop(aobj) && oopDesc::is_oop(bobj), "must be valid oops");
+ 
+   Handle ha(THREAD, aobj);
+   Handle hb(THREAD, bobj);
+   JavaValue result(T_BOOLEAN);
+   JavaCallArguments args;
+   args.push_oop(ha);
+   args.push_oop(hb);
+   methodHandle method(current, Universe::is_substitutable_method());
+   JavaCalls::call(&result, method, &args, THREAD);
+   if (HAS_PENDING_EXCEPTION) {
+     // Something really bad happened because isSubstitutable() should not throw exceptions
+     // If it is an error, just let it propagate
+     // If it is an exception, wrap it into an InternalError
+     if (!PENDING_EXCEPTION->is_a(vmClasses::Error_klass())) {
+       Handle e(THREAD, PENDING_EXCEPTION);
+       CLEAR_PENDING_EXCEPTION;
+       THROW_MSG_CAUSE_(vmSymbols::java_lang_InternalError(), "Internal error in substitutability test", e, false);
+     }
+   }
+   return result.get_jboolean();
+ JRT_END
  
  // Quicken instance-of and check-cast bytecodes
  JRT_ENTRY(void, InterpreterRuntime::quicken_io_cc(JavaThread* current))
    // Force resolving; quicken the bytecode
    LastFrameAccessor last_frame(current);

@@ -628,10 +882,14 @@
    ResourceMark rm(current);
    methodHandle mh = methodHandle(current, missingMethod);
    LinkResolver::throw_abstract_method_error(mh, recvKlass, THREAD);
  JRT_END
  
+ JRT_ENTRY(void, InterpreterRuntime::throw_InstantiationError(JavaThread* current))
+   THROW(vmSymbols::java_lang_InstantiationError());
+ JRT_END
+ 
  
  JRT_ENTRY(void, InterpreterRuntime::throw_IncompatibleClassChangeError(JavaThread* current))
    THROW(vmSymbols::java_lang_IncompatibleClassChangeError());
  JRT_END
  

@@ -661,12 +919,13 @@
    fieldDescriptor info;
    LastFrameAccessor last_frame(current);
    constantPoolHandle pool(current, last_frame.method()->constants());
    methodHandle m(current, last_frame.method());
    bool is_put    = (bytecode == Bytecodes::_putfield  || bytecode == Bytecodes::_nofast_putfield ||
-                     bytecode == Bytecodes::_putstatic);
+                     bytecode == Bytecodes::_putstatic || bytecode == Bytecodes::_withfield);
    bool is_static = (bytecode == Bytecodes::_getstatic || bytecode == Bytecodes::_putstatic);
+   bool is_inline_type  = bytecode == Bytecodes::_withfield;
  
    int field_index = last_frame.get_index_u2(bytecode);
    {
      JvmtiHideSingleStepping jhss(current);
      JavaThread* THREAD = current; // For exception macros.

@@ -708,18 +967,25 @@
    assert(!(has_initialized_final_update && !info.access_flags().is_final()), "Fields with initialized final updates must be final");
  
    Bytecodes::Code get_code = (Bytecodes::Code)0;
    Bytecodes::Code put_code = (Bytecodes::Code)0;
    if (!uninitialized_static) {
-     get_code = ((is_static) ? Bytecodes::_getstatic : Bytecodes::_getfield);
-     if ((is_put && !has_initialized_final_update) || !info.access_flags().is_final()) {
-       put_code = ((is_static) ? Bytecodes::_putstatic : Bytecodes::_putfield);
+     if (is_static) {
+       get_code = Bytecodes::_getstatic;
+     } else {
+       get_code = Bytecodes::_getfield;
+     }
+     if (is_put && is_inline_type) {
+         put_code = ((is_static) ? Bytecodes::_putstatic : Bytecodes::_withfield);
+     } else if ((is_put && !has_initialized_final_update) || !info.access_flags().is_final()) {
+         put_code = ((is_static) ? Bytecodes::_putstatic : Bytecodes::_putfield);
      }
    }
  
    ResolvedFieldEntry* entry = pool->resolved_field_entry_at(field_index);
-   entry->set_flags(info.access_flags().is_final(), info.access_flags().is_volatile());
+   entry->set_flags(info.access_flags().is_final(), info.access_flags().is_volatile(),
+                    info.is_flat(), info.is_null_free_inline_type());
    entry->fill_in(info.field_holder(), info.offset(),
                   checked_cast<u2>(info.index()), checked_cast<u1>(state),
                   static_cast<u1>(get_code), static_cast<u1>(put_code));
  }
  

@@ -972,10 +1238,11 @@
    switch (bytecode) {
    case Bytecodes::_getstatic:
    case Bytecodes::_putstatic:
    case Bytecodes::_getfield:
    case Bytecodes::_putfield:
+   case Bytecodes::_withfield:
      resolve_get_put(current, bytecode);
      break;
    case Bytecodes::_invokevirtual:
    case Bytecodes::_invokespecial:
    case Bytecodes::_invokestatic:

@@ -1163,33 +1430,36 @@
  JRT_END
  
  JRT_ENTRY(void, InterpreterRuntime::post_field_access(JavaThread* current, oopDesc* obj,
                                                        ResolvedFieldEntry *entry))
  
+   assert(entry->is_valid(), "Invalid ResolvedFieldEntry");
    // check the access_flags for the field in the klass
  
    InstanceKlass* ik = entry->field_holder();
    int index = entry->field_index();
    if (!ik->field_status(index).is_access_watched()) return;
  
    bool is_static = (obj == nullptr);
+   bool is_flat = entry->is_flat();
    HandleMark hm(current);
  
    Handle h_obj;
    if (!is_static) {
      // non-static field accessors have an object, but we need a handle
      h_obj = Handle(current, obj);
    }
    InstanceKlass* field_holder = entry->field_holder(); // HERE
-   jfieldID fid = jfieldIDWorkaround::to_jfieldID(field_holder, entry->field_offset(), is_static);
+   jfieldID fid = jfieldIDWorkaround::to_jfieldID(field_holder, entry->field_offset(), is_static, is_flat);
    LastFrameAccessor last_frame(current);
    JvmtiExport::post_field_access(current, last_frame.method(), last_frame.bcp(), field_holder, h_obj, fid);
  JRT_END
  
  JRT_ENTRY(void, InterpreterRuntime::post_field_modification(JavaThread* current, oopDesc* obj,
                                                              ResolvedFieldEntry *entry, jvalue *value))
  
+   assert(entry->is_valid(), "Invalid ResolvedFieldEntry");
    InstanceKlass* ik = entry->field_holder();
  
    // check the access_flags for the field in the klass
    int index = entry->field_index();
    // bail out if field modifications are not watched

@@ -1207,14 +1477,22 @@
      case atos: sig_type = JVM_SIGNATURE_CLASS;   break;
      case ltos: sig_type = JVM_SIGNATURE_LONG;    break;
      case dtos: sig_type = JVM_SIGNATURE_DOUBLE;  break;
      default:  ShouldNotReachHere(); return;
    }
+ 
+   // Both Q-signatures and L-signatures are mapped to atos
+   ik->field_is_null_free_inline_type(index);
+   if (entry->tos_state() == atos && ik->field_is_null_free_inline_type(index)) {
+     sig_type = JVM_SIGNATURE_PRIMITIVE_OBJECT;
+   }
+ 
    bool is_static = (obj == nullptr);
+   bool is_flat = entry->is_flat();
  
    HandleMark hm(current);
-   jfieldID fid = jfieldIDWorkaround::to_jfieldID(ik, entry->field_offset(), is_static);
+   jfieldID fid = jfieldIDWorkaround::to_jfieldID(ik, entry->field_offset(), is_static, is_flat);
    jvalue fvalue;
  #ifdef _LP64
    fvalue = *value;
  #else
    // Long/double values are stored unaligned and also noncontiguously with
< prev index next >