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     */
013    package org.jikesrvm.classloader;
014    
015    import java.util.StringTokenizer;
016    import org.jikesrvm.VM;
017    import org.jikesrvm.util.ImmutableEntryHashSetRVM;
018    import 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     */
033    public 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       */
086      public static synchronized MemberReference findOrCreate(TypeReference tRef, Atom mn, Atom md) {
087        MemberReference key;
088        if (md.isMethodDescriptor()) {
089          if (tRef.isArrayType() && !tRef.isUnboxedArrayType()) {
090            tRef = RVMType.JavaLangObjectType.getTypeRef();
091          }
092          key = new MethodReference(tRef, mn, md, nextId + 1);
093        } else {
094          key = new FieldReference(tRef, mn, md, nextId + 1);
095        }
096        MemberReference val = dictionary.get(key);
097        if (val != null) return val;
098        nextId++;
099        TableBasedDynamicLinker.ensureCapacity(key.id);
100        int column = key.id >> LOG_ROW_SIZE;
101        if (column == members.length) {
102          MemberReference[][] tmp = new MemberReference[column+1][];
103          for (int i=0; i < column; i++) {
104            tmp[i] = members[i];
105          }
106          members = tmp;
107          members[column] = new MemberReference[1 << LOG_ROW_SIZE];
108        }
109        members[column][key.id & ROW_MASK] = key;
110        dictionary.add(key);
111        return key;
112      }
113    
114      /**
115       * Given a StringTokenizer currently pointing to the start of a {@link
116       * MemberReference} (created by doing a toString() on a
117       * MemberReference), parse it and find/create the appropriate
118       * MemberReference. Consumes all of the tokens corresponding to the
119       * member reference.
120       */
121      public static MemberReference parse(StringTokenizer parser) {
122        return parse(parser, false);
123      }
124    
125      public static MemberReference parse(StringTokenizer parser, boolean boot) {
126        String clName = null;
127        try {
128          parser.nextToken(); // discard <
129          clName = parser.nextToken();
130          if ((!clName.equals(BootstrapClassLoader.myName)) && (boot)) {
131            return null;
132          }
133          Atom dc = Atom.findOrCreateUnicodeAtom(parser.nextToken());
134          Atom mn = Atom.findOrCreateUnicodeAtom(parser.nextToken());
135          Atom md = Atom.findOrCreateUnicodeAtom(parser.nextToken());
136          parser.nextToken(); // discard '>'
137          ClassLoader cl = null;
138          if (clName.equals(BootstrapClassLoader.myName)) {
139            cl = BootstrapClassLoader.getBootstrapClassLoader();
140          } else if (clName.equals(ApplicationClassLoader.myName)) {
141            cl = RVMClassLoader.getApplicationClassLoader();
142          } else {
143            cl = RVMClassLoader.findWorkableClassloader(dc);
144          }
145          TypeReference tref = TypeReference.findOrCreate(cl, dc);
146          return findOrCreate(tref, mn, md);
147        } catch (Exception e) {
148          VM.sysWriteln("Warning: error parsing for class "+clName+": "+e);
149          return null;
150        }
151      }
152    
153      //BEGIN HRM
154      public static int getNextId() {
155        return nextId;
156      }
157      //END HRM
158    
159      @Uninterruptible
160      public static MemberReference getMemberRef(int id) {
161        return members[id >> LOG_ROW_SIZE][id & ROW_MASK];
162      }
163    
164      /**
165       * @param tRef the type reference
166       * @param mn the field or method name
167       * @param d the field or method descriptor
168       * @param id the new ID of the member were a new member required (ignored in
169       *            .equals test)
170       */
171      protected MemberReference(TypeReference tRef, Atom mn, Atom d, int id) {
172        type = tRef;
173        name = mn;
174        descriptor = d;
175        this.id = id;
176      }
177    
178      /**
179       * @return the type reference component of this member reference
180       */
181      @Uninterruptible
182      public final TypeReference getType() {
183        return type;
184      }
185    
186      /**
187       * @return the member name component of this member reference
188       */
189      @Uninterruptible
190      public final Atom getName() {
191        return name;
192      }
193    
194      /**
195       * @return the descriptor component of this member reference
196       */
197      @Uninterruptible
198      public final Atom getDescriptor() {
199        return descriptor;
200      }
201    
202      /**
203       * @return the dynamic linking id to use for this member.
204       */
205      @Uninterruptible
206      public final int getId() {
207        return id;
208      }
209    
210      /**
211       * Is this member reference to a field?
212       */
213      @Uninterruptible
214      public final boolean isFieldReference() {
215        return this instanceof FieldReference;
216      }
217    
218      /**
219       * Is this member reference to a method?
220       */
221      @Uninterruptible
222      public final boolean isMethodReference() {
223        return this instanceof MethodReference;
224      }
225    
226      /**
227       * @return this cast to a FieldReference
228       */
229      @Uninterruptible
230      public final FieldReference asFieldReference() {
231        return (FieldReference) this;
232      }
233    
234      /**
235       * @return this cast to a MethodReference
236       */
237      @Uninterruptible
238      public final MethodReference asMethodReference() {
239        return (MethodReference) this;
240      }
241    
242      /**
243       * @return the RVMMember this reference resolves to if it is already known
244       * or {@code null} if it cannot be resolved without risking class loading.
245       */
246      public final RVMMember peekResolvedMember() {
247        if (isFieldReference()) {
248          return this.asFieldReference().peekResolvedField();
249        } else {
250          return this.asMethodReference().peekResolvedMethod();
251        }
252      }
253    
254      /**
255       * Force resolution and return the resolved member.
256       * Will cause classloading if necessary
257       */
258      public final RVMMember resolveMember() {
259        if (isFieldReference()) {
260          return this.asFieldReference().resolve();
261        } else {
262          return this.asMethodReference().resolve();
263        }
264      }
265    
266      /**
267       * Is dynamic linking code required to access "this" member when
268       * referenced from "that" method?
269       */
270      public final boolean needsDynamicLink(RVMMethod that) {
271        RVMMember resolvedThis = this.peekResolvedMember();
272    
273        if (resolvedThis == null) {
274          // can't tell because we haven't resolved the member reference
275          // sufficiently to even know exactly where it is declared.
276          return true;
277        }
278        RVMClass thisClass = resolvedThis.getDeclaringClass();
279    
280        if (thisClass == that.getDeclaringClass()) {
281          // Intra-class references don't need to be compiled with dynamic linking
282          // because they execute *after* class has been loaded/resolved/compiled.
283          return false;
284        }
285    
286        if (thisClass.isInitialized()) {
287          // No dynamic linking code is required to access this member
288          // because its size and offset are known and its class's static
289          // initializer has already run.
290          return false;
291        }
292    
293        if (isFieldReference() && thisClass.isResolved() && thisClass.getClassInitializerMethod() == null) {
294          // No dynamic linking code is required to access this field
295          // because its size and offset is known and its class has no static
296          // initializer, therefore its value need not be specially initialized
297          // (its default value of zero or null is sufficient).
298          return false;
299        }
300    
301        if (VM.writingBootImage && thisClass.isInBootImage()) {
302          // Loads, stores, and calls within boot image are compiled without dynamic
303          // linking code because all boot image classes are explicitly
304          // loaded/resolved/compiled and have had their static initializers
305          // run by the boot image writer.
306          if (VM.VerifyAssertions) VM._assert(thisClass.isResolved());
307          return false;
308        }
309    
310        // This member needs size and offset to be computed, or its class's static
311        // initializer needs to be run when the member is first "touched", so
312        // dynamic linking code is required to access the member.
313        return true;
314      }
315    
316      @Override
317      public final int hashCode() {
318        return type.hashCode() + name.hashCode() + descriptor.hashCode();
319      }
320    
321      @Override
322      public final boolean equals(Object other) {
323        if (other instanceof MemberReference) {
324          MemberReference that = (MemberReference) other;
325          return type == that.type && name == that.name && descriptor == that.descriptor;
326        } else {
327          return false;
328        }
329      }
330    
331      @Override
332      public final String toString() {
333        return "< " + type.getClassLoader() + ", " + type.getName() + ", " + name + ", " + descriptor + " >";
334      }
335    }