001/*
002 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003 *
004 *  This file is licensed to You under the Eclipse Public License (EPL);
005 *  You may not use this file except in compliance with the License. You
006 *  may obtain a copy of the License at
007 *
008 *      http://www.opensource.org/licenses/eclipse-1.0.php
009 *
010 *  See the COPYRIGHT.txt file distributed with this work for information
011 *  regarding copyright ownership.
012 */
013package org.jikesrvm.classloader;
014
015import java.util.StringTokenizer;
016import org.jikesrvm.VM;
017import org.jikesrvm.util.ImmutableEntryHashSetRVM;
018import org.vmmagic.pragma.Uninterruptible;
019
020/**
021 * A class to represent the reference in a class file to some
022 * member (field or method).
023 * A member reference is uniquely defined by
024 * <ul>
025 * <li> a type reference
026 * <li> a method name
027 * <li> a descriptor
028 * </ul>
029 * Resolving a MemberReference to a RVMMember can
030 * be an expensive operation.  Therefore we canonicalize
031 * MemberReference instances and cache the result of resolution.
032 */
033public abstract class MemberReference {
034
035  /**
036   * Used to canonicalize memberReferences
037   */
038  private static final ImmutableEntryHashSetRVM<MemberReference> dictionary =
039    new ImmutableEntryHashSetRVM<MemberReference>();
040
041  /**
042   * 2^LOG_ROW_SIZE is the number of elements per row
043   */
044  private static final int LOG_ROW_SIZE = 10;
045  /**
046   * Mask to ascertain row from id number
047   */
048  private static final int ROW_MASK = (1 << LOG_ROW_SIZE) - 1;
049  /**
050   * Dictionary of all MemberReference instances.
051   */
052  private static MemberReference[][] members = new MemberReference[16][1 << LOG_ROW_SIZE];
053
054  /**
055   * Used to assign ids.  Id 0 is not used to support usage of member reference id's in JNI.
056   */
057  private static int nextId = 1;
058
059  /**
060   * Unique id for the member reference (ignored in .equals comparison)
061   */
062  protected final int id;
063
064  /**
065   * The type reference
066   */
067  protected final TypeReference type;
068
069  /**
070   * The member name
071   */
072  protected final Atom name;
073
074  /**
075   * The descriptor
076   */
077  protected final Atom descriptor;
078
079  /**
080   * Find or create the canonical MemberReference instance for
081   * the given tuple.
082   * @param tRef the type reference
083   * @param mn the name of the member
084   * @param md the descriptor of the member
085   * @return a member reference, never {@code null}
086   */
087  public static synchronized MemberReference findOrCreate(TypeReference tRef, Atom mn, Atom md) {
088    MemberReference key;
089    if (md.isMethodDescriptor()) {
090      if (tRef.isArrayType() && !tRef.isUnboxedArrayType()) {
091        tRef = RVMType.JavaLangObjectType.getTypeRef();
092      }
093      key = new MethodReference(tRef, mn, md, nextId + 1);
094    } else {
095      key = new FieldReference(tRef, mn, md, nextId + 1);
096    }
097    MemberReference val = dictionary.get(key);
098    if (val != null) return val;
099    nextId++;
100    TableBasedDynamicLinker.ensureCapacity(key.id);
101    int column = key.id >> LOG_ROW_SIZE;
102    if (column == members.length) {
103      MemberReference[][] tmp = new MemberReference[column + 1][];
104      for (int i = 0; i < column; i++) {
105        tmp[i] = members[i];
106      }
107      members = tmp;
108      members[column] = new MemberReference[1 << LOG_ROW_SIZE];
109    }
110    members[column][key.id & ROW_MASK] = key;
111    dictionary.add(key);
112    return key;
113  }
114
115  /**
116   * Given a StringTokenizer currently pointing to the start of a {@link
117   * MemberReference} (created by doing a toString() on a
118   * MemberReference), parse it and find/create the appropriate
119   * MemberReference. Consumes all of the tokens corresponding to the
120   * member reference.
121   * @param parser a parser that fulfills the conditions described above
122   * @return a member reference or {@code null} if parsing fails
123   */
124  public static MemberReference parse(StringTokenizer parser) {
125    return parse(parser, false);
126  }
127
128  public static MemberReference parse(StringTokenizer parser, boolean boot) {
129    String clName = null;
130    try {
131      parser.nextToken(); // discard <
132      clName = parser.nextToken();
133      if ((!clName.equals(BootstrapClassLoader.myName)) && (boot)) {
134        return null;
135      }
136      Atom dc = Atom.findOrCreateUnicodeAtom(parser.nextToken());
137      Atom mn = Atom.findOrCreateUnicodeAtom(parser.nextToken());
138      Atom md = Atom.findOrCreateUnicodeAtom(parser.nextToken());
139      parser.nextToken(); // discard '>'
140      ClassLoader cl = null;
141      if (clName.equals(BootstrapClassLoader.myName)) {
142        cl = BootstrapClassLoader.getBootstrapClassLoader();
143      } else if (clName.equals(ApplicationClassLoader.myName)) {
144        cl = RVMClassLoader.getApplicationClassLoader();
145      } else {
146        cl = RVMClassLoader.findWorkableClassloader(dc);
147      }
148      TypeReference tref = TypeReference.findOrCreate(cl, dc);
149      return findOrCreate(tref, mn, md);
150    } catch (Exception e) {
151      VM.sysWriteln("Warning: error parsing for class " + clName + ": " + e);
152      return null;
153    }
154  }
155
156  //BEGIN HRM
157  public static int getNextId() {
158    return nextId;
159  }
160  //END HRM
161
162  @Uninterruptible
163  public static MemberReference getMemberRef(int id) {
164    return members[id >> LOG_ROW_SIZE][id & ROW_MASK];
165  }
166
167  @Uninterruptible
168  public static MethodReference getMethodRef(int id) {
169    return getMemberRef(id).asMethodReference();
170  }
171
172  @Uninterruptible
173  public static FieldReference getFieldRef(int id) {
174    return getMemberRef(id).asFieldReference();
175  }
176
177  /**
178   * @param tRef the type reference
179   * @param mn the field or method name
180   * @param d the field or method descriptor
181   * @param id the new ID of the member were a new member required (ignored in
182   *            .equals test)
183   */
184  protected MemberReference(TypeReference tRef, Atom mn, Atom d, int id) {
185    type = tRef;
186    name = mn;
187    descriptor = d;
188    this.id = id;
189  }
190
191  /**
192   * @return the type reference component of this member reference
193   */
194  @Uninterruptible
195  public final TypeReference getType() {
196    return type;
197  }
198
199  /**
200   * @return the member name component of this member reference
201   */
202  @Uninterruptible
203  public final Atom getName() {
204    return name;
205  }
206
207  /**
208   * @return the descriptor component of this member reference
209   */
210  @Uninterruptible
211  public final Atom getDescriptor() {
212    return descriptor;
213  }
214
215  /**
216   * @return the dynamic linking id to use for this member.
217   */
218  @Uninterruptible
219  public final int getId() {
220    return id;
221  }
222
223  /**
224   * @return {@code true} if this member references a field
225   */
226  @Uninterruptible
227  public final boolean isFieldReference() {
228    return this instanceof FieldReference;
229  }
230
231  /**
232   * @return {@code true} if this member references a method
233   */
234  @Uninterruptible
235  public final boolean isMethodReference() {
236    return this instanceof MethodReference;
237  }
238
239  /**
240   * @return this cast to a FieldReference
241   */
242  @Uninterruptible
243  public final FieldReference asFieldReference() {
244    return (FieldReference) this;
245  }
246
247  /**
248   * @return this cast to a MethodReference
249   */
250  @Uninterruptible
251  public final MethodReference asMethodReference() {
252    return (MethodReference) this;
253  }
254
255  /**
256   * @return the RVMMember this reference resolves to if it is already known
257   * or {@code null} if it cannot be resolved without risking class loading.
258   */
259  public final RVMMember peekResolvedMember() {
260    if (isFieldReference()) {
261      return this.asFieldReference().peekResolvedField();
262    } else {
263      return this.asMethodReference().peekResolvedMethod();
264    }
265  }
266
267  /**
268   * Forces resolution and returns the resolved member.
269   * Will cause classloading if necessary.
270   *
271   * @return the resolved member
272   */
273  public final RVMMember resolveMember() {
274    if (isFieldReference()) {
275      return this.asFieldReference().resolve();
276    } else {
277      return this.asMethodReference().resolve();
278    }
279  }
280
281  /**
282   * Is dynamic linking code required to access "this" member when
283   * referenced from "that" method?
284   * <p>
285   * This method is conservative, i.e. it will answer that we need linking
286   * if we don't know if it will be necessary.
287   *
288   * @param that the method to access
289   * @return {@code true} if dynamic linking could be necessary
290   */
291  public final boolean needsDynamicLink(RVMMethod that) {
292
293    RVMMember resolvedThis = this.peekResolvedMember();
294
295    if (resolvedThis == null) {
296      // can't tell because we haven't resolved the member reference
297      // sufficiently to even know exactly where it is declared.
298      return true;
299    }
300
301    RVMClass thisClass = resolvedThis.getDeclaringClass();
302
303    if (thisClass == that.getDeclaringClass()) {
304      // Intra-class references don't need to be compiled with dynamic linking
305      // because they execute *after* class has been loaded/resolved/compiled.
306      return false;
307    }
308
309    if (thisClass.isInitialized()) {
310      // No dynamic linking code is required to access this member
311      // because its size and offset are known and its class's static
312      // initializer has already run.
313      return false;
314    }
315
316    if (isFieldReference() && thisClass.isResolved() && thisClass.getClassInitializerMethod() == null) {
317      // No dynamic linking code is required to access this field
318      // because its size and offset is known and its class has no static
319      // initializer, therefore its value need not be specially initialized
320      // (its default value of zero or null is sufficient).
321      return false;
322    }
323
324    if (VM.writingBootImage && thisClass.isInBootImage()) {
325      // Loads, stores, and calls within boot image are compiled without dynamic
326      // linking code because all boot image classes are explicitly
327      // loaded/resolved/compiled and have had their static initializers
328      // run by the boot image writer.
329      if (VM.VerifyAssertions) VM._assert(thisClass.isResolved());
330      return false;
331    }
332
333    // This member needs size and offset to be computed, or its class's static
334    // initializer needs to be run when the member is first "touched", so
335    // dynamic linking code is required to access the member.
336    return true;
337  }
338
339  @Override
340  public final int hashCode() {
341    return type.hashCode() + name.hashCode() + descriptor.hashCode();
342  }
343
344  @Override
345  public final boolean equals(Object other) {
346    if (other instanceof MemberReference) {
347      MemberReference that = (MemberReference) other;
348      return type == that.type && name == that.name && descriptor == that.descriptor;
349    } else {
350      return false;
351    }
352  }
353
354  @Override
355  public final String toString() {
356    return "< " + type.getClassLoader() + ", " + type.getName() + ", " + name + ", " + descriptor + " >";
357  }
358}