Mercurial > hg > truffle
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 } |