/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* 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/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.classloader;
import java.util.StringTokenizer;
import org.jikesrvm.VM;
import org.jikesrvm.util.ImmutableEntryHashSetRVM;
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 MemberReference to a RVMMember can
* be an expensive operation. Therefore we canonicalize
* MemberReference instances and cache the result of resolution.
*/
public abstract class MemberReference {
/**
* Used to canonicalize memberReferences
*/
private static final ImmutableEntryHashSetRVM<MemberReference> dictionary =
new ImmutableEntryHashSetRVM<MemberReference>();
/**
* 2^LOG_ROW_SIZE is the number of elements per row
*/
private static final int LOG_ROW_SIZE = 10;
/**
* Mask to ascertain row from id number
*/
private static final int ROW_MASK = (1 << LOG_ROW_SIZE) - 1;
/**
* Dictionary of all MemberReference instances.
*/
private static MemberReference[][] members = new MemberReference[16][1 << LOG_ROW_SIZE];
/**
* Used to assign ids. Id 0 is not used to support usage of member reference id's in JNI.
*/
private static int nextId = 1;
/**
* Unique id for the member reference (ignored in .equals comparison)
*/
protected final int id;
/**
* The type reference
*/
protected final TypeReference type;
/**
* The member name
*/
protected final Atom name;
/**
* The descriptor
*/
protected final Atom descriptor;
/**
* Find or create the canonical 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
* @return a member reference, never {@code null}
*/
public static synchronized MemberReference findOrCreate(TypeReference tRef, Atom mn, Atom md) {
MemberReference key;
if (md.isMethodDescriptor()) {
if (tRef.isArrayType() && !tRef.isUnboxedArrayType()) {
tRef = RVMType.JavaLangObjectType.getTypeRef();
}
key = new MethodReference(tRef, mn, md, nextId + 1);
} else {
key = new FieldReference(tRef, mn, md, nextId + 1);
}
MemberReference val = dictionary.get(key);
if (val != null) return val;
nextId++;
TableBasedDynamicLinker.ensureCapacity(key.id);
int column = key.id >> LOG_ROW_SIZE;
if (column == members.length) {
MemberReference[][] tmp = new MemberReference[column + 1][];
for (int i = 0; i < column; i++) {
tmp[i] = members[i];
}
members = tmp;
members[column] = new MemberReference[1 << LOG_ROW_SIZE];
}
members[column][key.id & ROW_MASK] = key;
dictionary.add(key);
return key;
}
/**
* Given a StringTokenizer currently pointing to the start of a {@link
* MemberReference} (created by doing a toString() on a
* MemberReference), parse it and find/create the appropriate
* MemberReference. Consumes all of the tokens corresponding to the
* member reference.
* @param parser a parser that fulfills the conditions described above
* @return a member reference or {@code null} if parsing fails
*/
public static MemberReference parse(StringTokenizer parser) {
return parse(parser, false);
}
public static MemberReference parse(StringTokenizer parser, boolean boot) {
String clName = null;
try {
parser.nextToken(); // discard <
clName = parser.nextToken();
if ((!clName.equals(BootstrapClassLoader.myName)) && (boot)) {
return null;
}
Atom dc = Atom.findOrCreateUnicodeAtom(parser.nextToken());
Atom mn = Atom.findOrCreateUnicodeAtom(parser.nextToken());
Atom md = Atom.findOrCreateUnicodeAtom(parser.nextToken());
parser.nextToken(); // discard '>'
ClassLoader cl = null;
if (clName.equals(BootstrapClassLoader.myName)) {
cl = BootstrapClassLoader.getBootstrapClassLoader();
} else if (clName.equals(ApplicationClassLoader.myName)) {
cl = RVMClassLoader.getApplicationClassLoader();
} else {
cl = RVMClassLoader.findWorkableClassloader(dc);
}
TypeReference tref = TypeReference.findOrCreate(cl, dc);
return findOrCreate(tref, mn, md);
} catch (Exception e) {
VM.sysWriteln("Warning: error parsing for class " + clName + ": " + e);
return null;
}
}
//BEGIN HRM
public static int getNextId() {
return nextId;
}
//END HRM
@Uninterruptible
public static MemberReference getMemberRef(int id) {
return members[id >> LOG_ROW_SIZE][id & ROW_MASK];
}
@Uninterruptible
public static MethodReference getMethodRef(int id) {
return getMemberRef(id).asMethodReference();
}
@Uninterruptible
public static FieldReference getFieldRef(int id) {
return getMemberRef(id).asFieldReference();
}
/**
* @param tRef the type reference
* @param mn the field or method name
* @param d the field or method descriptor
* @param id the new ID of the member were a new member required (ignored in
* .equals test)
*/
protected MemberReference(TypeReference tRef, Atom mn, Atom d, int id) {
type = tRef;
name = mn;
descriptor = d;
this.id = id;
}
/**
* @return the type reference component of this member reference
*/
@Uninterruptible
public final TypeReference getType() {
return type;
}
/**
* @return the member name component of this member reference
*/
@Uninterruptible
public final Atom getName() {
return name;
}
/**
* @return the descriptor component of this member reference
*/
@Uninterruptible
public final Atom getDescriptor() {
return descriptor;
}
/**
* @return the dynamic linking id to use for this member.
*/
@Uninterruptible
public final int getId() {
return id;
}
/**
* @return {@code true} if this member references a field
*/
@Uninterruptible
public final boolean isFieldReference() {
return this instanceof FieldReference;
}
/**
* @return {@code true} if this member references a method
*/
@Uninterruptible
public final boolean isMethodReference() {
return this instanceof MethodReference;
}
/**
* @return this cast to a FieldReference
*/
@Uninterruptible
public final FieldReference asFieldReference() {
return (FieldReference) this;
}
/**
* @return this cast to a MethodReference
*/
@Uninterruptible
public final MethodReference asMethodReference() {
return (MethodReference) this;
}
/**
* @return the RVMMember this reference resolves to if it is already known
* or {@code null} if it cannot be resolved without risking class loading.
*/
public final RVMMember peekResolvedMember() {
if (isFieldReference()) {
return this.asFieldReference().peekResolvedField();
} else {
return this.asMethodReference().peekResolvedMethod();
}
}
/**
* Forces resolution and returns the resolved member.
* Will cause classloading if necessary.
*
* @return the resolved member
*/
public final RVMMember resolveMember() {
if (isFieldReference()) {
return this.asFieldReference().resolve();
} else {
return this.asMethodReference().resolve();
}
}
/**
* Is dynamic linking code required to access "this" member when
* referenced from "that" method?
* <p>
* This method is conservative, i.e. it will answer that we need linking
* if we don't know if it will be necessary.
*
* @param that the method to access
* @return {@code true} if dynamic linking could be necessary
*/
public final boolean needsDynamicLink(RVMMethod that) {
RVMMember 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;
}
RVMClass 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()) {
// 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() && 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());
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;
}
@Override
public final int hashCode() {
return type.hashCode() + name.hashCode() + descriptor.hashCode();
}
@Override
public final boolean equals(Object other) {
if (other instanceof MemberReference) {
MemberReference that = (MemberReference) other;
return type == that.type && name == that.name && descriptor == that.descriptor;
} else {
return false;
}
}
@Override
public final String toString() {
return "< " + type.getClassLoader() + ", " + type.getName() + ", " + name + ", " + descriptor + " >";
}
}