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}