+// A helper for find_field that knows how to recursively search
+// superclasses and interfaces.
+_Jv_Field *
+_Jv_Linker::find_field_helper (jclass search, _Jv_Utf8Const *name,
+ _Jv_Utf8Const *type_name, jclass type,
+ jclass *declarer)
+{
+ while (search)
+ {
+ // From 5.4.3.2. First search class itself.
+ for (int i = 0; i < search->field_count; ++i)
+ {
+ _Jv_Field *field = &search->fields[i];
+ if (! _Jv_equalUtf8Consts (field->name, name))
+ continue;
+
+ // Checks for the odd situation where we were able to retrieve the
+ // field's class from signature but the resolution of the field itself
+ // failed which means a different class was resolved.
+ if (type != NULL)
+ {
+ try
+ {
+ resolve_field (field, search->loader);
+ }
+ catch (java::lang::Throwable *exc)
+ {
+ java::lang::LinkageError *le = new java::lang::LinkageError
+ (JvNewStringLatin1
+ ("field type mismatch with different loaders"));
+
+ le->initCause(exc);
+
+ throw le;
+ }
+ }
+
+ // Note that we compare type names and not types. This is
+ // bizarre, but we do it because we want to find a field
+ // (and terminate the search) if it has the correct
+ // descriptor -- but then later reject it if the class
+ // loader check results in different classes. We can't just
+ // pass in the descriptor and check that way, because when
+ // the field is already resolved there is no easy way to
+ // find its descriptor again.
+ if ((field->isResolved ()
+ ? _Jv_equalUtf8Classnames (type_name, field->type->name)
+ : _Jv_equalUtf8Classnames (type_name,
+ (_Jv_Utf8Const *) field->type)))
+ {
+ *declarer = search;
+ return field;
+ }
+ }
+
+ // Next search direct interfaces.
+ for (int i = 0; i < search->interface_count; ++i)
+ {
+ _Jv_Field *result = find_field_helper (search->interfaces[i], name,
+ type_name, type, declarer);
+ if (result)
+ return result;
+ }
+
+ // Now search superclass.
+ search = search->superclass;
+ }
+
+ return NULL;
+}
+
+bool
+_Jv_Linker::has_field_p (jclass search, _Jv_Utf8Const *field_name)
+{
+ for (int i = 0; i < search->field_count; ++i)
+ {
+ _Jv_Field *field = &search->fields[i];
+ if (_Jv_equalUtf8Consts (field->name, field_name))
+ return true;
+ }
+ return false;
+}
+
+// Find a field.
+// KLASS is the class that is requesting the field.
+// OWNER is the class in which the field should be found.
+// FIELD_TYPE_NAME is the type descriptor for the field.
+// Fill FOUND_CLASS with the address of the class in which the field
+// is actually declared.
+// This function does the class loader type checks, and
+// also access checks. Returns the field, or throws an
+// exception on error.
+_Jv_Field *
+_Jv_Linker::find_field (jclass klass, jclass owner,
+ jclass *found_class,
+ _Jv_Utf8Const *field_name,
+ _Jv_Utf8Const *field_type_name)
+{
+ // FIXME: this allocates a _Jv_Utf8Const each time. We should make
+ // it cheaper.
+ // Note: This call will resolve the primitive type names ("Z", "B", ...) to
+ // their Java counterparts ("boolean", "byte", ...) if accessed via
+ // field_type->name later. Using these variants of the type name is in turn
+ // important for the find_field_helper function. However if the class
+ // resolution failed then we can only use the already given type name.
+ jclass field_type
+ = _Jv_FindClassFromSignatureNoException (field_type_name->chars(),
+ klass->loader);
+
+ _Jv_Field *the_field
+ = find_field_helper (owner, field_name,
+ (field_type
+ ? field_type->name :
+ field_type_name ),
+ field_type, found_class);
+
+ if (the_field == 0)
+ {
+ java::lang::StringBuffer *sb = new java::lang::StringBuffer();
+ sb->append(JvNewStringLatin1("field "));
+ sb->append(owner->getName());
+ sb->append(JvNewStringLatin1("."));
+ sb->append(_Jv_NewStringUTF(field_name->chars()));
+ sb->append(JvNewStringLatin1(" was not found."));
+ throw new java::lang::NoSuchFieldError (sb->toString());
+ }
+
+ // Accept it when the field's class could not be resolved.
+ if (field_type == NULL)
+ // Silently ignore that we were not able to retrieve the type to make it
+ // possible to run code which does not access this field.
+ return the_field;
+
+ if (_Jv_CheckAccess (klass, *found_class, the_field->flags))
+ {
+ // Note that the field returned by find_field_helper is always
+ // resolved. However, we still use the constraint mechanism
+ // because this may affect other lookups.
+ _Jv_CheckOrCreateLoadingConstraint (field_type, (*found_class)->loader);
+ }
+ else
+ {
+ java::lang::StringBuffer *sb
+ = new java::lang::StringBuffer ();
+ sb->append(klass->getName());
+ sb->append(JvNewStringLatin1(": "));
+ sb->append((*found_class)->getName());
+ sb->append(JvNewStringLatin1("."));
+ sb->append(_Jv_NewStringUtf8Const (field_name));
+ throw new java::lang::IllegalAccessError(sb->toString());
+ }
+
+ return the_field;
+}
+
+// Check loading constraints for method.
+void
+_Jv_Linker::check_loading_constraints (_Jv_Method *method, jclass self_class,
+ jclass other_class)
+{
+ JArray<jclass> *klass_args;
+ jclass klass_return;
+
+ _Jv_GetTypesFromSignature (method, self_class, &klass_args, &klass_return);
+ jclass *klass_arg = elements (klass_args);
+ java::lang::ClassLoader *found_loader = other_class->loader;
+
+ _Jv_CheckOrCreateLoadingConstraint (klass_return, found_loader);
+ for (int i = 0; i < klass_args->length; i++)
+ _Jv_CheckOrCreateLoadingConstraint (*(klass_arg++), found_loader);
+}
+
+_Jv_Method *
+_Jv_Linker::resolve_method_entry (jclass klass, jclass &found_class,
+ int class_index, int name_and_type_index,
+ bool init, bool is_iface)
+{
+ _Jv_Constants *pool = &klass->constants;
+ jclass owner = resolve_pool_entry (klass, class_index).clazz;
+
+ if (init && owner != klass)
+ _Jv_InitClass (owner);
+
+ _Jv_ushort name_index, type_index;
+ _Jv_loadIndexes (&pool->data[name_and_type_index],
+ name_index,
+ type_index);
+
+ _Jv_Utf8Const *method_name = pool->data[name_index].utf8;
+ _Jv_Utf8Const *method_signature = pool->data[type_index].utf8;
+
+ _Jv_Method *the_method = 0;
+ found_class = 0;
+
+ // We're going to cache a pointer to the _Jv_Method object
+ // when we find it. So, to ensure this doesn't get moved from
+ // beneath us, we first put all the needed Miranda methods
+ // into the target class.
+ wait_for_state (klass, JV_STATE_LOADED);
+
+ // First search the class itself.
+ the_method = search_method_in_class (owner, klass,
+ method_name, method_signature);
+
+ if (the_method != 0)
+ {
+ found_class = owner;
+ goto end_of_method_search;
+ }
+
+ // If we are resolving an interface method, search the
+ // interface's superinterfaces (A superinterface is not an
+ // interface's superclass - a superinterface is implemented by
+ // the interface).
+ if (is_iface)
+ {
+ _Jv_ifaces ifaces;
+ ifaces.count = 0;
+ ifaces.len = 4;
+ ifaces.list = (jclass *) _Jv_Malloc (ifaces.len
+ * sizeof (jclass *));
+
+ get_interfaces (owner, &ifaces);
+
+ for (int i = 0; i < ifaces.count; i++)
+ {
+ jclass cls = ifaces.list[i];
+ the_method = search_method_in_class (cls, klass, method_name,
+ method_signature);
+ if (the_method != 0)
+ {
+ found_class = cls;
+ break;
+ }
+ }
+
+ _Jv_Free (ifaces.list);
+
+ if (the_method != 0)
+ goto end_of_method_search;
+ }
+
+ // Finally, search superclasses.
+ the_method = (search_method_in_superclasses
+ (owner->getSuperclass (), klass, method_name,
+ method_signature, &found_class));
+
+
+ end_of_method_search:
+ if (the_method == 0)
+ {
+ java::lang::StringBuffer *sb = new java::lang::StringBuffer();
+ sb->append(JvNewStringLatin1("method "));
+ sb->append(owner->getName());
+ sb->append(JvNewStringLatin1("."));
+ sb->append(_Jv_NewStringUTF(method_name->chars()));
+ sb->append(JvNewStringLatin1(" with signature "));
+ sb->append(_Jv_NewStringUTF(method_signature->chars()));
+ sb->append(JvNewStringLatin1(" was not found."));
+ throw new java::lang::NoSuchMethodError (sb->toString());
+ }
+
+ // if (found_class->loader != klass->loader), then we must actually
+ // check that the types of arguments correspond. JVMS 5.4.3.3.
+ if (found_class->loader != klass->loader)
+ check_loading_constraints (the_method, klass, found_class);
+
+ return the_method;
+}
+
+_Jv_Mutex_t _Jv_Linker::resolve_mutex;
+
+void
+_Jv_Linker::init (void)
+{
+ _Jv_MutexInit (&_Jv_Linker::resolve_mutex);
+}
+
+// Locking in resolve_pool_entry is somewhat subtle. Constant
+// resolution is idempotent, so it doesn't matter if two threads
+// resolve the same entry. However, it is important that we always
+// write the resolved flag and the data together, atomically. It is
+// also important that we read them atomically.