001/* 002 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package jdk.internal.jvmci.meta; 024 025import java.lang.reflect.*; 026import java.util.*; 027import java.util.function.*; 028import java.util.stream.*; 029 030import jdk.internal.jvmci.meta.MethodIdHolder.*; 031 032/** 033 * A map whose keys are {@link MethodIdHolder}s. This data structure can be used for mapping 034 * identifiers to methods without requiring eager resolution of the latter (e.g., to 035 * {@link ResolvedJavaMethod}s) and has retrieval as fast as array indexing. The constraints on 036 * using such a map are: 037 * <ul> 038 * <li>at most one value can be added for any key</li> 039 * <li>no more entries can be added after the first {@linkplain #get(MethodIdHolder) retrieval}</li> 040 * </ul> 041 * 042 * @param <V> the type of the values in the map 043 */ 044public class MethodIdMap<V> { 045 046 /** 047 * Key for a method. 048 */ 049 public static class MethodKey<T> { 050 final boolean isStatic; 051 052 /** 053 * This method is optional. This is used for new API methods not present in previous JDK 054 * versions. 055 */ 056 final boolean isOptional; 057 058 final Class<?> declaringClass; 059 final String name; 060 final Class<?>[] argumentTypes; 061 final T value; 062 int id; 063 064 MethodKey(T data, boolean isStatic, boolean isOptional, Class<?> declaringClass, String name, Class<?>... argumentTypes) { 065 assert isStatic || argumentTypes[0] == declaringClass; 066 this.value = data; 067 this.isStatic = isStatic; 068 this.isOptional = isOptional; 069 this.declaringClass = declaringClass; 070 this.name = name; 071 this.argumentTypes = argumentTypes; 072 assert isOptional || resolveJava() != null; 073 } 074 075 @Override 076 public boolean equals(Object obj) { 077 if (obj instanceof MethodKey) { 078 MethodKey<?> that = (MethodKey<?>) obj; 079 boolean res = this.name.equals(that.name) && this.declaringClass.equals(that.declaringClass) && Arrays.equals(this.argumentTypes, that.argumentTypes); 080 assert !res || this.isStatic == that.isStatic; 081 return res; 082 } 083 return false; 084 } 085 086 public int getDeclaredParameterCount() { 087 return isStatic ? argumentTypes.length : argumentTypes.length - 1; 088 } 089 090 @Override 091 public int hashCode() { 092 // Replay compilation mandates use of stable hash codes 093 return declaringClass.getName().hashCode() ^ name.hashCode(); 094 } 095 096 private MethodIdHolder resolve(MetaAccessProvider metaAccess) { 097 Executable method = resolveJava(); 098 if (method == null) { 099 return null; 100 } 101 return (MethodIdHolder) metaAccess.lookupJavaMethod(method); 102 } 103 104 private Executable resolveJava() { 105 try { 106 Executable res; 107 Class<?>[] parameterTypes = isStatic ? argumentTypes : Arrays.copyOfRange(argumentTypes, 1, argumentTypes.length); 108 if (name.equals("<init>")) { 109 res = declaringClass.getDeclaredConstructor(parameterTypes); 110 } else { 111 res = declaringClass.getDeclaredMethod(name, parameterTypes); 112 } 113 assert Modifier.isStatic(res.getModifiers()) == isStatic; 114 return res; 115 } catch (NoSuchMethodException | SecurityException e) { 116 if (isOptional) { 117 return null; 118 } 119 throw new InternalError(e); 120 } 121 } 122 123 @Override 124 public String toString() { 125 StringBuilder sb = new StringBuilder(declaringClass.getName()).append('.').append(name).append('('); 126 for (Class<?> p : argumentTypes) { 127 if (sb.charAt(sb.length() - 1) != '(') { 128 sb.append(", "); 129 } 130 sb.append(p.getSimpleName()); 131 } 132 return sb.append(')').toString(); 133 } 134 } 135 136 private final MetaAccessProvider metaAccess; 137 138 /** 139 * Initial list of entries. 140 */ 141 private final List<MethodKey<V>> registrations; 142 143 /** 144 * Entry array that is initialized upon first call to {@link #get(MethodIdHolder)}. 145 * 146 * Note: this must be volatile since double-checked locking is used to initialize it 147 */ 148 private volatile V[] entries; 149 150 /** 151 * The minimum {@linkplain MethodIdHolder#getMethodId() id} for a key in this map. 152 */ 153 private int minId = Integer.MAX_VALUE; 154 155 public MethodIdMap(MetaAccessProvider metaAccess) { 156 this.metaAccess = metaAccess; 157 this.registrations = new ArrayList<>(INITIAL_CAPACITY); 158 } 159 160 private static final int INITIAL_CAPACITY = 64; 161 162 /** 163 * Adds an entry to this map for a specified method. 164 * 165 * @param value value to be associated with the specified method 166 * @param isStatic specifies if the method is static 167 * @param isOptional specifies if the method is optional 168 * @param declaringClass the class declaring the method 169 * @param name the name of the method 170 * @param argumentTypes the argument types of the method. Element 0 of this array must be 171 * {@code declaringClass} iff the method is non-static. 172 * @return an object representing the method 173 */ 174 public MethodKey<V> put(V value, boolean isStatic, boolean isOptional, Class<?> declaringClass, String name, Class<?>... argumentTypes) { 175 assert isStatic || argumentTypes[0] == declaringClass; 176 MethodKey<V> methodKey = new MethodKey<>(value, isStatic, isOptional, declaringClass, name, argumentTypes); 177 assert entries == null : "registration is closed"; 178 assert !registrations.contains(methodKey) : "a value is already registered for " + methodKey; 179 registrations.add(methodKey); 180 return methodKey; 181 } 182 183 @SuppressWarnings("unchecked") 184 protected V[] allocateEntries(int length) { 185 return (V[]) new Object[length]; 186 } 187 188 /** 189 * Determines if a method denoted by a given {@link MethodKey} is in this map. 190 */ 191 public boolean containsKey(MethodKey<V> key) { 192 return registrations.contains(key); 193 } 194 195 public V get(MethodIdHolder method) { 196 if (entries == null) { 197 createEntries(); 198 } 199 200 int id = method.getMethodId(); 201 int index = id - minId; 202 return index >= 0 && index < entries.length ? entries[index] : null; 203 } 204 205 public void createEntries() { 206 // 'assignIds' synchronizes on a global lock which ensures thread safe 207 // allocation of identifiers across all MethodIdHolder objects 208 MethodIdHolder.assignIds(new Consumer<MethodIdAllocator>() { 209 210 public void accept(MethodIdAllocator idAllocator) { 211 if (entries == null) { 212 if (registrations.isEmpty()) { 213 entries = allocateEntries(0); 214 } else { 215 int max = Integer.MIN_VALUE; 216 for (MethodKey<V> methodKey : registrations) { 217 MethodIdHolder m = methodKey.resolve(metaAccess); 218 if (m == null) { 219 assert methodKey.isOptional; 220 methodKey.id = -1; 221 } else { 222 int id = idAllocator.assignId(m); 223 if (id < minId) { 224 minId = id; 225 } 226 if (id > max) { 227 max = id; 228 } 229 methodKey.id = id; 230 } 231 } 232 233 int length = (max - minId) + 1; 234 entries = allocateEntries(length); 235 for (MethodKey<V> methodKey : registrations) { 236 if (methodKey.id == -1) { 237 assert methodKey.isOptional; 238 } else { 239 int index = methodKey.id - minId; 240 entries[index] = methodKey.value; 241 } 242 } 243 } 244 } 245 } 246 }); 247 } 248 249 @Override 250 public String toString() { 251 return registrations.stream().map(MethodKey::toString).collect(Collectors.joining(", ")); 252 } 253 254 public MetaAccessProvider getMetaAccess() { 255 return metaAccess; 256 } 257 258 public int size() { 259 return registrations.size(); 260 } 261}