comparison graal/com.oracle.jvmci.meta/src/com/oracle/jvmci/meta/MethodIdMap.java @ 21556:48c1ebd24120

renamed com.oracle.graal.api[meta|code] modules to com.oracle.jvmci.[meta|code] (JBS:GRAAL-53)
author Doug Simon <doug.simon@oracle.com>
date Wed, 27 May 2015 00:36:16 +0200
parents graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/MethodIdMap.java@0627ebc2a3ea
children
comparison
equal deleted inserted replaced
21555:d12eaef9af72 21556:48c1ebd24120
1 /*
2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23 package com.oracle.jvmci.meta;
24
25 import java.lang.reflect.*;
26 import java.util.*;
27 import java.util.function.*;
28 import java.util.stream.*;
29
30 import com.oracle.jvmci.meta.MethodIdHolder.MethodIdAllocator;
31
32 /**
33 * A map whose keys are {@link MethodIdHolder}s. This data structure can be used for mapping
34 * identifiers to methods without requiring eager resolution of the latter (e.g., to
35 * {@link ResolvedJavaMethod}s) and has retrieval as fast as array indexing. The constraints on
36 * using such a map are:
37 * <ul>
38 * <li>at most one value can be added for any key</li>
39 * <li>no more entries can be added after the first {@linkplain #get(MethodIdHolder) retrieval}</li>
40 * </ul>
41 *
42 * @param <V> the type of the values in the map
43 */
44 public class MethodIdMap<V> {
45
46 /**
47 * Key for a method.
48 */
49 public static class MethodKey<T> {
50 final boolean isStatic;
51 final Class<?> declaringClass;
52 final String name;
53 final Class<?>[] argumentTypes;
54 final T value;
55 int id;
56
57 MethodKey(T data, boolean isStatic, Class<?> declaringClass, String name, Class<?>... argumentTypes) {
58 assert isStatic || argumentTypes[0] == declaringClass;
59 this.value = data;
60 this.isStatic = isStatic;
61 this.declaringClass = declaringClass;
62 this.name = name;
63 this.argumentTypes = argumentTypes;
64 assert resolveJava() != null;
65 }
66
67 @Override
68 public boolean equals(Object obj) {
69 if (obj instanceof MethodKey) {
70 MethodKey<?> that = (MethodKey<?>) obj;
71 boolean res = this.name.equals(that.name) && this.declaringClass.equals(that.declaringClass) && Arrays.equals(this.argumentTypes, that.argumentTypes);
72 assert !res || this.isStatic == that.isStatic;
73 return res;
74 }
75 return false;
76 }
77
78 public int getDeclaredParameterCount() {
79 return isStatic ? argumentTypes.length : argumentTypes.length - 1;
80 }
81
82 @Override
83 public int hashCode() {
84 // Replay compilation mandates use of stable hash codes
85 return declaringClass.getName().hashCode() ^ name.hashCode();
86 }
87
88 private MethodIdHolder resolve(MetaAccessProvider metaAccess) {
89 return (MethodIdHolder) metaAccess.lookupJavaMethod(resolveJava());
90 }
91
92 private Executable resolveJava() {
93 try {
94 Executable res;
95 Class<?>[] parameterTypes = isStatic ? argumentTypes : Arrays.copyOfRange(argumentTypes, 1, argumentTypes.length);
96 if (name.equals("<init>")) {
97 res = declaringClass.getDeclaredConstructor(parameterTypes);
98 } else {
99 res = declaringClass.getDeclaredMethod(name, parameterTypes);
100 }
101 assert Modifier.isStatic(res.getModifiers()) == isStatic;
102 return res;
103 } catch (NoSuchMethodException | SecurityException e) {
104 throw new InternalError(e);
105 }
106 }
107
108 @Override
109 public String toString() {
110 StringBuilder sb = new StringBuilder(declaringClass.getName()).append('.').append(name).append('(');
111 for (Class<?> p : argumentTypes) {
112 if (sb.charAt(sb.length() - 1) != '(') {
113 sb.append(", ");
114 }
115 sb.append(p.getSimpleName());
116 }
117 return sb.append(')').toString();
118 }
119 }
120
121 private final MetaAccessProvider metaAccess;
122
123 /**
124 * Initial list of entries.
125 */
126 private final List<MethodKey<V>> registrations;
127
128 /**
129 * Entry array that is initialized upon first call to {@link #get(MethodIdHolder)}.
130 *
131 * Note: this must be volatile since double-checked locking is used to initialize it
132 */
133 private volatile V[] entries;
134
135 /**
136 * The minimum {@linkplain MethodIdHolder#getMethodId() id} for a key in this map.
137 */
138 private int minId = Integer.MAX_VALUE;
139
140 public MethodIdMap(MetaAccessProvider metaAccess) {
141 this.metaAccess = metaAccess;
142 this.registrations = new ArrayList<>(INITIAL_CAPACITY);
143 }
144
145 private static final int INITIAL_CAPACITY = 64;
146
147 /**
148 * Adds an entry to this map for a specified method.
149 *
150 * @param value value to be associated with the specified method
151 * @param isStatic specifies if the method is static
152 * @param declaringClass the class declaring the method
153 * @param name the name of the method
154 * @param argumentTypes the argument types of the method. Element 0 of this array must be
155 * {@code declaringClass} iff the method is non-static.
156 * @return an object representing the method
157 */
158 public MethodKey<V> put(V value, boolean isStatic, Class<?> declaringClass, String name, Class<?>... argumentTypes) {
159 assert isStatic || argumentTypes[0] == declaringClass;
160 MethodKey<V> methodKey = new MethodKey<>(value, isStatic, declaringClass, name, argumentTypes);
161 assert entries == null : "registration is closed";
162 assert !registrations.contains(methodKey) : "a value is already registered for " + methodKey;
163 registrations.add(methodKey);
164 return methodKey;
165 }
166
167 @SuppressWarnings("unchecked")
168 protected V[] allocateEntries(int length) {
169 return (V[]) new Object[length];
170 }
171
172 /**
173 * Determines if a method denoted by a given {@link MethodKey} is in this map.
174 */
175 public boolean containsKey(MethodKey<V> key) {
176 return registrations.contains(key);
177 }
178
179 public V get(MethodIdHolder method) {
180 if (entries == null) {
181 createEntries();
182 }
183
184 int id = method.getMethodId();
185 int index = id - minId;
186 return index >= 0 && index < entries.length ? entries[index] : null;
187 }
188
189 public void createEntries() {
190 // 'assignIds' synchronizes on a global lock which ensures thread safe
191 // allocation of identifiers across all MethodIdHolder objects
192 MethodIdHolder.assignIds(new Consumer<MethodIdAllocator>() {
193
194 public void accept(MethodIdAllocator idAllocator) {
195 if (entries == null) {
196 if (registrations.isEmpty()) {
197 entries = allocateEntries(0);
198 } else {
199 int max = Integer.MIN_VALUE;
200 for (MethodKey<V> methodKey : registrations) {
201 MethodIdHolder m = methodKey.resolve(metaAccess);
202 int id = idAllocator.assignId(m);
203 if (id < minId) {
204 minId = id;
205 }
206 if (id > max) {
207 max = id;
208 }
209 methodKey.id = id;
210 }
211
212 int length = (max - minId) + 1;
213 entries = allocateEntries(length);
214 for (MethodKey<V> m : registrations) {
215 int index = m.id - minId;
216 entries[index] = m.value;
217 }
218 }
219 }
220 }
221 });
222 }
223
224 @Override
225 public String toString() {
226 return registrations.stream().map(MethodKey::toString).collect(Collectors.joining(", "));
227 }
228
229 public MetaAccessProvider getMetaAccess() {
230 return metaAccess;
231 }
232
233 public int size() {
234 return registrations.size();
235 }
236 }