/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Common Public License (CPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/cpl1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.classloader;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import org.jikesrvm.VM;
import org.jikesrvm.util.VM_HashSet;
import org.vmmagic.pragma.Uninterruptible;
/**
* A class to represent the reference in a class file to some
* member (field or method).
* A member reference is uniquely defined by
* <ul>
* <li> a type reference
* <li> a method name
* <li> a descriptor
* </ul>
* Resolving a VM_MemberReference to a VM_Member can
* be an expensive operation. Therefore we canonicalize
* VM_MemberReference instances and cache the result of resolution.
*/
public abstract class VM_MemberReference {
/**
* Used to canonicalize memberReferences
*/
private static VM_HashSet<VM_MemberReference> dictionary = new VM_HashSet<VM_MemberReference>();
/**
* Dictionary of all VM_MemberReference instances.
*/
private static VM_MemberReference[] members = new VM_MemberReference[16000];
/**
* Used to assign ids. Id 0 is not used to support usage of member reference id's in JNI.
*/
private static int nextId = 1;
/**
* The type reference
*/
protected final VM_TypeReference type;
/**
* The member name
*/
protected final VM_Atom name;
/**
* The descriptor
*/
protected final VM_Atom descriptor;
/**
* Unique id for the member reference
*/
protected int id;
/**
* Find or create the canonical VM_MemberReference instance for
* the given tuple.
* @param tRef the type reference
* @param mn the name of the member
* @param md the descriptor of the member
*/
public static synchronized VM_MemberReference findOrCreate(VM_TypeReference tRef, VM_Atom mn, VM_Atom md) {
VM_MemberReference key;
if (md.isMethodDescriptor()) {
if (tRef.isArrayType() && !tRef.isUnboxedArrayType()) {
tRef = VM_Type.JavaLangObjectType.getTypeRef();
}
key = new VM_MethodReference(tRef, mn, md);
} else {
key = new VM_FieldReference(tRef, mn, md);
}
VM_MemberReference val = dictionary.get(key);
if (val != null) return val;
key.id = nextId++;
VM_TableBasedDynamicLinker.ensureCapacity(key.id);
if (key.id == members.length) {
VM_MemberReference[] tmp = new VM_MemberReference[members.length * 2];
System.arraycopy(members, 0, tmp, 0, members.length);
members = tmp;
}
members[key.id] = key;
dictionary.add(key);
return key;
}
/**
* Given a StringTokenizer currently pointing to the start of a {@link
* VM_MemberReference} (created by doing a toString() on a
* VM_MemberReference), parse it and find/create the appropriate
* VM_MemberReference. Consumes all of the tokens corresponding to the
* member reference.
*/
public static VM_MemberReference parse(StringTokenizer parser) {
return parse(parser, false);
}
public static VM_MemberReference parse(StringTokenizer parser, boolean boot) {
try {
parser.nextToken(); // discard <
String clName = parser.nextToken();
if ((!clName.equals(VM_BootstrapClassLoader.myName)) && (boot)) {
return null;
}
VM_Atom dc = VM_Atom.findOrCreateUnicodeAtom(parser.nextToken());
VM_Atom mn = VM_Atom.findOrCreateUnicodeAtom(parser.nextToken());
VM_Atom md = VM_Atom.findOrCreateUnicodeAtom(parser.nextToken());
parser.nextToken(); // discard '>'
ClassLoader cl;
if (clName.equals(VM_BootstrapClassLoader.myName)) {
cl = VM_BootstrapClassLoader.getBootstrapClassLoader();
} else if (clName.equals(VM_ApplicationClassLoader.myName)) {
cl = VM_ClassLoader.getApplicationClassLoader();
} else {
try {
ClassLoader appCl = VM_ClassLoader.getApplicationClassLoader();
Class<?> cls = appCl.loadClass(clName.substring(0, clName.indexOf('@')));
cl = (ClassLoader) cls.newInstance();
} catch (Exception ex) {
throw new InternalError("Unable to load class with custom class loader: " + ex);
}
}
VM_TypeReference tref = VM_TypeReference.findOrCreate(cl, dc);
return findOrCreate(tref, mn, md);
} catch (NoSuchElementException e) {
return null;
}
}
//BEGIN HRM
public static int getNextId() {
return nextId;
}
//END HRM
@Uninterruptible
public static VM_MemberReference getMemberRef(int id) {
return members[id];
}
/**
* @param tRef the type reference
* @param mn the field or method name
* @param d the field or method descriptor
*/
protected VM_MemberReference(VM_TypeReference tRef, VM_Atom mn, VM_Atom d) {
type = tRef;
name = mn;
descriptor = d;
}
/**
* @return the type reference component of this member reference
*/
@Uninterruptible
public final VM_TypeReference getType() {
return type;
}
/**
* @return the member name component of this member reference
*/
@Uninterruptible
public final VM_Atom getName() {
return name;
}
/**
* @return the descriptor component of this member reference
*/
@Uninterruptible
public final VM_Atom getDescriptor() {
return descriptor;
}
/**
* @return the dynamic linking id to use for this member.
*/
@Uninterruptible
public final int getId() {
return id;
}
/**
* Is this member reference to a field?
*/
@Uninterruptible
public final boolean isFieldReference() {
return this instanceof VM_FieldReference;
}
/**
* Is this member reference to a method?
*/
@Uninterruptible
public final boolean isMethodReference() {
return this instanceof VM_MethodReference;
}
/**
* @return this cast to a VM_FieldReference
*/
@Uninterruptible
public final VM_FieldReference asFieldReference() {
return (VM_FieldReference) this;
}
/**
* @return this cast to a VM_MethodReference
*/
@Uninterruptible
public final VM_MethodReference asMethodReference() {
return (VM_MethodReference) this;
}
/**
* @return the VM_Member this reference resolves to if it is already known
* or null if it cannot be resolved without risking class loading.
*/
public final VM_Member peekResolvedMember() {
if (isFieldReference()) {
return this.asFieldReference().peekResolvedField(false);
} else {
return this.asMethodReference().peekResolvedMethod(false);
}
}
/**
* Force resolution and return the resolved member.
* Will cause classloading if necessary
*/
public final VM_Member resolveMember(boolean forSubArch) {
if (isFieldReference()) {
return this.asFieldReference().resolve(forSubArch);
} else {
return this.asMethodReference().resolve(forSubArch);
}
}
/**
* Is dynamic linking code required to access "this" member when
* referenced from "that" method?
*/
public final boolean needsDynamicLink(VM_Method that, boolean forSubArch) {
VM_Member resolvedThis = this.peekResolvedMember();
if (resolvedThis == null) {
// can't tell because we haven't resolved the member reference
// sufficiently to even know exactly where it is declared.
return true;
}
VM_Class thisClass = resolvedThis.getDeclaringClass();
if (thisClass == that.getDeclaringClass()) {
// Intra-class references don't need to be compiled with dynamic linking
// because they execute *after* class has been loaded/resolved/compiled.
return false;
}
if (thisClass.isInitialized(forSubArch)) {
// No dynamic linking code is required to access this member
// because its size and offset are known and its class's static
// initializer has already run.
return false;
}
if (isFieldReference() && thisClass.isResolved(forSubArch) && thisClass.getClassInitializerMethod() == null) {
// No dynamic linking code is required to access this field
// because its size and offset is known and its class has no static
// initializer, therefore its value need not be specially initialized
// (its default value of zero or null is sufficient).
return false;
}
if (VM.writingBootImage && thisClass.isInBootImage()) {
// Loads, stores, and calls within boot image are compiled without dynamic
// linking code because all boot image classes are explicitly
// loaded/resolved/compiled and have had their static initializers
// run by the boot image writer.
if (VM.VerifyAssertions) VM._assert(thisClass.isResolved(forSubArch));
return false;
}
// This member needs size and offset to be computed, or its class's static
// initializer needs to be run when the member is first "touched", so
// dynamic linking code is required to access the member.
return true;
}
public final int hashCode() {
return type.hashCode() + name.hashCode() + descriptor.hashCode();
}
public final boolean equals(Object other) {
if (other instanceof VM_MemberReference) {
VM_MemberReference that = (VM_MemberReference) other;
return type == that.type && name == that.name && descriptor == that.descriptor;
} else {
return false;
}
}
public final String toString() {
return "< " + type.getClassLoader() + ", " + type.getName() + ", " + name + ", " + descriptor + " >";
}
}