< prev index next >
src/share/vm/oops/constantPool.cpp
Print this page
*** 45,66 ****
#include "runtime/signature.hpp"
#include "runtime/vframe.hpp"
#include "utilities/copy.hpp"
ConstantPool* ConstantPool::allocate(ClassLoaderData* loader_data, int length, TRAPS) {
- // Tags are RW but comment below applies to tags also.
Array<u1>* tags = MetadataFactory::new_writeable_array<u1>(loader_data, length, 0, CHECK_NULL);
-
int size = ConstantPool::size(length);
!
! // CDS considerations:
! // Allocate read-write but may be able to move to read-only at dumping time
! // if all the klasses are resolved. The only other field that is writable is
! // the resolved_references array, which is recreated at startup time.
! // But that could be moved to InstanceKlass (although a pain to access from
! // assembly code). Maybe it could be moved to the cpCache which is RW.
! return new (loader_data, size, false, MetaspaceObj::ConstantPoolType, THREAD) ConstantPool(tags);
}
#ifdef ASSERT
// MetaspaceObj allocation invariant is calloc equivalent memory
--- 45,57 ----
#include "runtime/signature.hpp"
#include "runtime/vframe.hpp"
#include "utilities/copy.hpp"
ConstantPool* ConstantPool::allocate(ClassLoaderData* loader_data, int length, TRAPS) {
Array<u1>* tags = MetadataFactory::new_writeable_array<u1>(loader_data, length, 0, CHECK_NULL);
int size = ConstantPool::size(length);
! return new (loader_data, size, true, MetaspaceObj::ConstantPoolType, THREAD) ConstantPool(tags);
}
#ifdef ASSERT
// MetaspaceObj allocation invariant is calloc equivalent memory
*** 78,103 ****
#endif
ConstantPool::ConstantPool(Array<u1>* tags) :
_tags(tags),
! _length(tags->length()),
! _flags(0) {
assert(_tags != NULL, "invariant");
assert(tags->length() == _length, "invariant");
assert(tag_array_is_zero_initialized(tags), "invariant");
! assert(0 == _flags, "invariant");
assert(0 == version(), "invariant");
assert(NULL == _pool_holder, "invariant");
}
void ConstantPool::deallocate_contents(ClassLoaderData* loader_data) {
! MetadataFactory::free_metadata(loader_data, cache());
! set_cache(NULL);
MetadataFactory::free_array<u2>(loader_data, reference_map());
set_reference_map(NULL);
MetadataFactory::free_array<jushort>(loader_data, operands());
set_operands(NULL);
release_C_heap_structures();
--- 69,98 ----
#endif
ConstantPool::ConstantPool(Array<u1>* tags) :
_tags(tags),
! _length(tags->length()) {
assert(_tags != NULL, "invariant");
assert(tags->length() == _length, "invariant");
assert(tag_array_is_zero_initialized(tags), "invariant");
! assert(0 == flags(), "invariant");
assert(0 == version(), "invariant");
assert(NULL == _pool_holder, "invariant");
}
void ConstantPool::deallocate_contents(ClassLoaderData* loader_data) {
! if (cache() != NULL) {
MetadataFactory::free_array<u2>(loader_data, reference_map());
set_reference_map(NULL);
+ MetadataFactory::free_metadata(loader_data, cache());
+ set_cache(NULL);
+ }
+
+ MetadataFactory::free_array<Klass*>(loader_data, resolved_klasses());
+ set_resolved_klasses(NULL);
MetadataFactory::free_array<jushort>(loader_data, operands());
set_operands(NULL);
release_C_heap_structures();
*** 111,121 ****
// walk constant pool and decrement symbol reference counts
unreference_symbols();
}
objArrayOop ConstantPool::resolved_references() const {
! return (objArrayOop)JNIHandles::resolve(_resolved_references);
}
// Create resolved_references array and mapping array for original cp indexes
// The ldc bytecode was rewritten to have the resolved reference array index so need a way
// to map it back for resolving and some unlikely miscellaneous uses.
--- 106,116 ----
// walk constant pool and decrement symbol reference counts
unreference_symbols();
}
objArrayOop ConstantPool::resolved_references() const {
! return (objArrayOop)JNIHandles::resolve(_cache->resolved_references());
}
// Create resolved_references array and mapping array for original cp indexes
// The ldc bytecode was rewritten to have the resolved reference array index so need a way
// to map it back for resolving and some unlikely miscellaneous uses.
*** 148,160 ****
--- 143,227 ----
Handle refs_handle (THREAD, (oop)stom); // must handleize.
set_resolved_references(loader_data->add_handle(refs_handle));
}
}
+ void ConstantPool::allocate_resolved_klasses(ClassLoaderData* loader_data, int num_klasses, TRAPS) {
+ // A ConstantPool can't possibly have 0xffff valid class entries,
+ // because entry #0 must be CONSTANT_Invalid, and each class entry must refer to a UTF8
+ // entry for the class's name. So at most we will have 0xfffe class entries.
+ // This allows us to use 0xffff (ConstantPool::_temp_resolved_klass_index) to indicate
+ // UnresolvedKlass entries that are temporarily created during class redefinition.
+ assert(num_klasses < CPKlassSlot::_temp_resolved_klass_index, "sanity");
+ assert(resolved_klasses() == NULL, "sanity");
+ Array<Klass*>* rk = MetadataFactory::new_writeable_array<Klass*>(loader_data, num_klasses, CHECK);
+ set_resolved_klasses(rk);
+ }
+
+ void ConstantPool::initialize_unresolved_klasses(ClassLoaderData* loader_data, TRAPS) {
+ int len = length();
+ int num_klasses = 0;
+ for (int i = 1; i <len; i++) {
+ switch (tag_at(i).value()) {
+ case JVM_CONSTANT_ClassIndex:
+ {
+ const int class_index = klass_index_at(i);
+ unresolved_klass_at_put(i, class_index, num_klasses++);
+ }
+ break;
+ #ifndef PRODUCT
+ case JVM_CONSTANT_Class:
+ case JVM_CONSTANT_UnresolvedClass:
+ case JVM_CONSTANT_UnresolvedClassInError:
+ // All of these should have been reverted back to ClassIndex before calling
+ // this function.
+ ShouldNotReachHere();
+ #endif
+ }
+ }
+ allocate_resolved_klasses(loader_data, num_klasses, THREAD);
+ }
+
+ // Anonymous class support:
+ void ConstantPool::klass_at_put(int class_index, int name_index, int resolved_klass_index, Klass* k, Symbol* name) {
+ assert(is_within_bounds(class_index), "index out of bounds");
+ assert(is_within_bounds(name_index), "index out of bounds");
+ assert((resolved_klass_index & 0xffff0000) == 0, "must be");
+ *int_at_addr(class_index) =
+ build_int_from_shorts((jushort)resolved_klass_index, (jushort)name_index);
+
+ symbol_at_put(name_index, name);
+ name->increment_refcount();
+ Klass** adr = resolved_klasses()->adr_at(resolved_klass_index);
+ OrderAccess::release_store_ptr((Klass* volatile *)adr, k);
+
+ // The interpreter assumes when the tag is stored, the klass is resolved
+ // and the Klass* non-NULL, so we need hardware store ordering here.
+ if (k != NULL) {
+ release_tag_at_put(class_index, JVM_CONSTANT_Class);
+ } else {
+ release_tag_at_put(class_index, JVM_CONSTANT_UnresolvedClass);
+ }
+ }
+
+ // Anonymous class support:
+ void ConstantPool::klass_at_put(int class_index, Klass* k) {
+ CPKlassSlot kslot = klass_slot_at(class_index);
+ int resolved_klass_index = kslot.resolved_klass_index();
+ int name_index = kslot.name_index();
+ Symbol* name = symbol_at(name_index);
+ guarantee(name == k->name(), "Invalid class name for anonymous");
+
+ Klass** adr = resolved_klasses()->adr_at(resolved_klass_index);
+ OrderAccess::release_store_ptr((Klass* volatile *)adr, k);
+ }
+
// CDS support. Create a new resolved_references array.
void ConstantPool::restore_unshareable_info(TRAPS) {
assert(is_constantPool(), "ensure C++ vtable is restored");
+ assert(on_stack(), "should always be set for shared constant pools");
+ assert(is_shared(), "should always be set for shared constant pools");
// Only create the new resolved references array if it hasn't been attempted before
if (resolved_references() != NULL) return;
// restore the C++ vtable from the shared archive
*** 178,187 ****
--- 245,260 ----
// Save the length for restoration. It is not necessarily the same length
// as reference_map.length() if invokedynamic is saved.
set_resolved_reference_length(
resolved_references() != NULL ? resolved_references()->length() : 0);
set_resolved_references(NULL);
+
+ // Shared ConstantPools are in the RO region, so the _flags cannot be modified.
+ // The _on_stack flag is used to prevent ConstantPools from deallocation during
+ // class redefinition. Since shared ConstantPools cannot be deallocated anyway,
+ // we always set _on_stack to true to avoid having to change _flags during runtime.
+ _flags |= (_on_stack | _is_shared);
}
int ConstantPool::cp_to_object_index(int cp_index) {
// this is harder don't do this so much.
int i = reference_map()->find(cp_index);
*** 227,241 ****
assert(THREAD->is_Java_thread(), "must be a Java thread");
// A resolved constantPool entry will contain a Klass*, otherwise a Symbol*.
// It is not safe to rely on the tag bit's here, since we don't have a lock, and
// the entry and tag is not updated atomicly.
! CPSlot entry = this_cp->slot_at(which);
! if (entry.is_resolved()) {
! assert(entry.get_klass()->is_klass(), "must be");
! // Already resolved - return entry.
! return entry.get_klass();
}
// This tag doesn't change back to unresolved class unless at a safepoint.
if (this_cp->tag_at(which).is_unresolved_klass_in_error()) {
// The original attempt to resolve this constant pool entry failed so find the
--- 300,317 ----
assert(THREAD->is_Java_thread(), "must be a Java thread");
// A resolved constantPool entry will contain a Klass*, otherwise a Symbol*.
// It is not safe to rely on the tag bit's here, since we don't have a lock, and
// the entry and tag is not updated atomicly.
! CPKlassSlot kslot = this_cp->klass_slot_at(which);
! int resolved_klass_index = kslot.resolved_klass_index();
! int name_index = kslot.name_index();
! assert(this_cp->tag_at(name_index).is_symbol(), "sanity");
!
! Klass* klass = this_cp->resolved_klasses()->at(resolved_klass_index);
! if (klass != NULL) {
! return klass;
}
// This tag doesn't change back to unresolved class unless at a safepoint.
if (this_cp->tag_at(which).is_unresolved_klass_in_error()) {
// The original attempt to resolve this constant pool entry failed so find the
*** 249,259 ****
throw_resolution_error(this_cp, which, CHECK_0);
ShouldNotReachHere();
}
Handle mirror_handle;
! Symbol* name = entry.get_symbol();
Handle loader (THREAD, this_cp->pool_holder()->class_loader());
Handle protection_domain (THREAD, this_cp->pool_holder()->protection_domain());
Klass* k = SystemDictionary::resolve_or_fail(name, loader, protection_domain, true, THREAD);
if (!HAS_PENDING_EXCEPTION) {
// preserve the resolved klass from unloading
--- 325,335 ----
throw_resolution_error(this_cp, which, CHECK_0);
ShouldNotReachHere();
}
Handle mirror_handle;
! Symbol* name = this_cp->symbol_at(name_index);
Handle loader (THREAD, this_cp->pool_holder()->class_loader());
Handle protection_domain (THREAD, this_cp->pool_holder()->protection_domain());
Klass* k = SystemDictionary::resolve_or_fail(name, loader, protection_domain, true, THREAD);
if (!HAS_PENDING_EXCEPTION) {
// preserve the resolved klass from unloading
*** 268,281 ****
if (save_resolution_error) {
save_and_throw_exception(this_cp, which, constantTag(JVM_CONSTANT_UnresolvedClass), CHECK_NULL);
// If CHECK_NULL above doesn't return the exception, that means that
// some other thread has beaten us and has resolved the class.
// To preserve old behavior, we return the resolved class.
! entry = this_cp->resolved_klass_at(which);
! assert(entry.is_resolved(), "must be resolved if exception was cleared");
! assert(entry.get_klass()->is_klass(), "must be resolved to a klass");
! return entry.get_klass();
} else {
return NULL; // return the pending exception
}
}
--- 344,356 ----
if (save_resolution_error) {
save_and_throw_exception(this_cp, which, constantTag(JVM_CONSTANT_UnresolvedClass), CHECK_NULL);
// If CHECK_NULL above doesn't return the exception, that means that
// some other thread has beaten us and has resolved the class.
// To preserve old behavior, we return the resolved class.
! klass = this_cp->resolved_klasses()->at(resolved_klass_index);
! assert(klass != NULL, "must be resolved if exception was cleared");
! return klass;
} else {
return NULL; // return the pending exception
}
}
*** 285,314 ****
// logging for class+resolve.
if (log_is_enabled(Debug, class, resolve)){
trace_class_resolution(this_cp, k);
}
! this_cp->klass_at_put(which, k);
! entry = this_cp->resolved_klass_at(which);
! assert(entry.is_resolved() && entry.get_klass()->is_klass(), "must be resolved at this point");
! return entry.get_klass();
}
// Does not update ConstantPool* - to avoid any exception throwing. Used
// by compiler and exception handling. Also used to avoid classloads for
// instanceof operations. Returns NULL if the class has not been loaded or
// if the verification of constant pool failed
Klass* ConstantPool::klass_at_if_loaded(const constantPoolHandle& this_cp, int which) {
! CPSlot entry = this_cp->slot_at(which);
! if (entry.is_resolved()) {
! assert(entry.get_klass()->is_klass(), "must be");
! return entry.get_klass();
} else {
- assert(entry.is_unresolved(), "must be either symbol or klass");
Thread *thread = Thread::current();
! Symbol* name = entry.get_symbol();
oop loader = this_cp->pool_holder()->class_loader();
oop protection_domain = this_cp->pool_holder()->protection_domain();
Handle h_prot (thread, protection_domain);
Handle h_loader (thread, loader);
Klass* k = SystemDictionary::find(name, h_loader, h_prot, thread);
--- 360,395 ----
// logging for class+resolve.
if (log_is_enabled(Debug, class, resolve)){
trace_class_resolution(this_cp, k);
}
! Klass** adr = this_cp->resolved_klasses()->adr_at(resolved_klass_index);
! OrderAccess::release_store_ptr((Klass* volatile *)adr, k);
! // The interpreter assumes when the tag is stored, the klass is resolved
! // and the Klass* stored in _resolved_klasses is non-NULL, so we need
! // hardware store ordering here.
! this_cp->release_tag_at_put(which, JVM_CONSTANT_Class);
! return k;
}
// Does not update ConstantPool* - to avoid any exception throwing. Used
// by compiler and exception handling. Also used to avoid classloads for
// instanceof operations. Returns NULL if the class has not been loaded or
// if the verification of constant pool failed
Klass* ConstantPool::klass_at_if_loaded(const constantPoolHandle& this_cp, int which) {
! CPKlassSlot kslot = this_cp->klass_slot_at(which);
! int resolved_klass_index = kslot.resolved_klass_index();
! int name_index = kslot.name_index();
! assert(this_cp->tag_at(name_index).is_symbol(), "sanity");
!
! Klass* k = this_cp->resolved_klasses()->at(resolved_klass_index);
! if (k != NULL) {
! return k;
} else {
Thread *thread = Thread::current();
! Symbol* name = this_cp->symbol_at(name_index);
oop loader = this_cp->pool_holder()->class_loader();
oop protection_domain = this_cp->pool_holder()->protection_domain();
Handle h_prot (thread, protection_domain);
Handle h_loader (thread, loader);
Klass* k = SystemDictionary::find(name, h_loader, h_prot, thread);
*** 482,507 ****
Klass* ConstantPool::klass_ref_at(int which, TRAPS) {
return klass_at(klass_ref_index_at(which), THREAD);
}
-
Symbol* ConstantPool::klass_name_at(int which) const {
! assert(tag_at(which).is_unresolved_klass() || tag_at(which).is_klass(),
! "Corrupted constant pool");
! // A resolved constantPool entry will contain a Klass*, otherwise a Symbol*.
! // It is not safe to rely on the tag bit's here, since we don't have a lock, and the entry and
! // tag is not updated atomicly.
! CPSlot entry = slot_at(which);
! if (entry.is_resolved()) {
! // Already resolved - return entry's name.
! assert(entry.get_klass()->is_klass(), "must be");
! return entry.get_klass()->name();
! } else {
! assert(entry.is_unresolved(), "must be either symbol or klass");
! return entry.get_symbol();
! }
}
Symbol* ConstantPool::klass_ref_at_noresolve(int which) {
jint ref_index = klass_ref_index_at(which);
return klass_at_noresolve(ref_index);
--- 563,574 ----
Klass* ConstantPool::klass_ref_at(int which, TRAPS) {
return klass_at(klass_ref_index_at(which), THREAD);
}
Symbol* ConstantPool::klass_name_at(int which) const {
! return symbol_at(klass_slot_at(which).name_index());
}
Symbol* ConstantPool::klass_ref_at_noresolve(int which) {
jint ref_index = klass_ref_index_at(which);
return klass_at_noresolve(ref_index);
*** 848,858 ****
}
// Iterate over symbols and decrement ones which are Symbol*s
// This is done during GC.
! // Only decrement the UTF8 symbols. Unresolved classes and strings point to
// these symbols but didn't increment the reference count.
void ConstantPool::unreference_symbols() {
for (int index = 1; index < length(); index++) { // Index 0 is unused
constantTag tag = tag_at(index);
if (tag.is_symbol()) {
--- 915,925 ----
}
// Iterate over symbols and decrement ones which are Symbol*s
// This is done during GC.
! // Only decrement the UTF8 symbols. Strings point to
// these symbols but didn't increment the reference count.
void ConstantPool::unreference_symbols() {
for (int index = 1; index < length(); index++) { // Index 0 is unused
constantTag tag = tag_at(index);
if (tag.is_symbol()) {
*** 1229,1244 ****
const constantPoolHandle& to_cp, int to_i,
TRAPS) {
int tag = from_cp->tag_at(from_i).value();
switch (tag) {
- case JVM_CONSTANT_Class:
- {
- Klass* k = from_cp->klass_at(from_i, CHECK);
- to_cp->klass_at_put(to_i, k);
- } break;
-
case JVM_CONSTANT_ClassIndex:
{
jint ki = from_cp->klass_index_at(from_i);
to_cp->klass_index_at_put(to_i, ki);
} break;
--- 1296,1305 ----
*** 1303,1324 ****
{
jint si = from_cp->string_index_at(from_i);
to_cp->string_index_at_put(to_i, si);
} break;
case JVM_CONSTANT_UnresolvedClass:
case JVM_CONSTANT_UnresolvedClassInError:
{
! // Can be resolved after checking tag, so check the slot first.
! CPSlot entry = from_cp->slot_at(from_i);
! if (entry.is_resolved()) {
! assert(entry.get_klass()->is_klass(), "must be");
! // Already resolved
! to_cp->klass_at_put(to_i, entry.get_klass());
! } else {
! to_cp->unresolved_klass_at_put(to_i, entry.get_symbol());
! }
} break;
case JVM_CONSTANT_String:
{
Symbol* s = from_cp->unresolved_string_at(from_i);
--- 1364,1381 ----
{
jint si = from_cp->string_index_at(from_i);
to_cp->string_index_at_put(to_i, si);
} break;
+ case JVM_CONSTANT_Class:
case JVM_CONSTANT_UnresolvedClass:
case JVM_CONSTANT_UnresolvedClassInError:
{
! // Revert to JVM_CONSTANT_ClassIndex
! int name_index = from_cp->klass_slot_at(from_i).name_index();
! assert(from_cp->tag_at(name_index).is_symbol(), "sanity");
! to_cp->klass_index_at_put(to_i, name_index);
} break;
case JVM_CONSTANT_String:
{
Symbol* s = from_cp->unresolved_string_at(from_i);
*** 1366,1376 ****
ShouldNotReachHere();
} break;
}
} // end copy_entry_to()
-
// Search constant pool search_cp for an entry that matches this
// constant pool's entry at pattern_i. Returns the index of a
// matching entry or zero (0) if there is no matching entry.
int ConstantPool::find_matching_entry(int pattern_i,
const constantPoolHandle& search_cp, TRAPS) {
--- 1423,1432 ----
*** 1822,1838 ****
--- 1878,1897 ----
void ConstantPool::set_on_stack(const bool value) {
if (value) {
// Only record if it's not already set.
if (!on_stack()) {
+ assert(!is_shared(), "should always be set for shared constant pools");
_flags |= _on_stack;
MetadataOnStackMark::record(this);
}
} else {
// Clearing is done single-threadedly.
+ if (!is_shared()) {
_flags &= ~_on_stack;
}
+ }
}
// JSR 292 support for patching constant pool oops after the class is linked and
// the oop array for resolved references are created.
// We can't do this during classfile parsing, which is how the other indexes are
*** 1903,1912 ****
--- 1962,1972 ----
st->print_cr(" - holder: " INTPTR_FORMAT, p2i(pool_holder()));
}
st->print_cr(" - cache: " INTPTR_FORMAT, p2i(cache()));
st->print_cr(" - resolved_references: " INTPTR_FORMAT, p2i(resolved_references()));
st->print_cr(" - reference_map: " INTPTR_FORMAT, p2i(reference_map()));
+ st->print_cr(" - resolved_klasses: " INTPTR_FORMAT, p2i(resolved_klasses()));
for (int index = 1; index < length(); index++) { // Index 0 is unused
((ConstantPool*)this)->print_entry_on(index, st);
switch (tag_at(index).value()) {
case JVM_CONSTANT_Long :
*** 1964,1980 ****
st->print(" signature_index=%d", signature_ref_index_at(index));
break;
case JVM_CONSTANT_Utf8 :
symbol_at(index)->print_value_on(st);
break;
case JVM_CONSTANT_UnresolvedClass : // fall-through
case JVM_CONSTANT_UnresolvedClassInError: {
! CPSlot entry = slot_at(index);
! if (entry.is_resolved()) {
! entry.get_klass()->print_value_on(st);
} else {
! entry.get_symbol()->print_value_on(st);
}
}
break;
case JVM_CONSTANT_MethodHandle :
case JVM_CONSTANT_MethodHandleInError :
--- 2024,2051 ----
st->print(" signature_index=%d", signature_ref_index_at(index));
break;
case JVM_CONSTANT_Utf8 :
symbol_at(index)->print_value_on(st);
break;
+ case JVM_CONSTANT_ClassIndex: {
+ int name_index = *int_at_addr(index);
+ st->print("klass_index=%d ", name_index);
+ symbol_at(name_index)->print_value_on(st);
+ }
+ break;
case JVM_CONSTANT_UnresolvedClass : // fall-through
case JVM_CONSTANT_UnresolvedClassInError: {
! CPKlassSlot kslot = klass_slot_at(index);
! int resolved_klass_index = kslot.resolved_klass_index();
! int name_index = kslot.name_index();
! assert(tag_at(name_index).is_symbol(), "sanity");
!
! Klass* klass = resolved_klasses()->at(resolved_klass_index);
! if (klass != NULL) {
! klass->print_value_on(st);
} else {
! symbol_at(name_index)->print_value_on(st);
}
}
break;
case JVM_CONSTANT_MethodHandle :
case JVM_CONSTANT_MethodHandleInError :
*** 2042,2063 ****
void ConstantPool::verify_on(outputStream* st) {
guarantee(is_constantPool(), "object must be constant pool");
for (int i = 0; i< length(); i++) {
constantTag tag = tag_at(i);
! CPSlot entry = slot_at(i);
! if (tag.is_klass()) {
! if (entry.is_resolved()) {
! guarantee(entry.get_klass()->is_klass(), "should be klass");
! }
! } else if (tag.is_unresolved_klass()) {
! if (entry.is_resolved()) {
! guarantee(entry.get_klass()->is_klass(), "should be klass");
! }
} else if (tag.is_symbol()) {
guarantee(entry.get_symbol()->refcount() != 0, "should have nonzero reference count");
} else if (tag.is_string()) {
guarantee(entry.get_symbol()->refcount() != 0, "should have nonzero reference count");
}
}
if (cache() != NULL) {
// Note: cache() can be NULL before a class is completely setup or
--- 2113,2129 ----
void ConstantPool::verify_on(outputStream* st) {
guarantee(is_constantPool(), "object must be constant pool");
for (int i = 0; i< length(); i++) {
constantTag tag = tag_at(i);
! if (tag.is_klass() || tag.is_unresolved_klass()) {
! guarantee(klass_name_at(i)->refcount() != 0, "should have nonzero reference count");
} else if (tag.is_symbol()) {
+ CPSlot entry = slot_at(i);
guarantee(entry.get_symbol()->refcount() != 0, "should have nonzero reference count");
} else if (tag.is_string()) {
+ CPSlot entry = slot_at(i);
guarantee(entry.get_symbol()->refcount() != 0, "should have nonzero reference count");
}
}
if (cache() != NULL) {
// Note: cache() can be NULL before a class is completely setup or
< prev index next >