8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.lang.invoke;
27
28 import java.util.Arrays;
29 import static java.lang.invoke.LambdaForm.*;
30 import static java.lang.invoke.LambdaForm.BasicType.*;
31 import static java.lang.invoke.MethodHandleImpl.Intrinsic;
32 import java.util.Collections;
33 import java.util.concurrent.ConcurrentHashMap;
34
35 import sun.invoke.util.Wrapper;
36
37 /** Transforms on LFs.
38 * A lambda-form editor can derive new LFs from its base LF.
39 * The editor can cache derived LFs, which simplifies the reuse of their underlying bytecodes.
40 * To support this caching, a LF has an optional pointer to its editor.
41 */
42 class LambdaFormEditor {
43 final LambdaForm lambdaForm;
44
45 private LambdaFormEditor(LambdaForm lambdaForm) {
46 this.lambdaForm = lambdaForm;
47 }
48
49 // Factory method.
50 static LambdaFormEditor lambdaFormEditor(LambdaForm lambdaForm) {
51 // TO DO: Consider placing intern logic here, to cut down on duplication.
52 // lambdaForm = findPreexistingEquivalent(lambdaForm)
53 return new LambdaFormEditor(lambdaForm);
54 }
55
56 /** A description of a cached transform, possibly associated with the result of the transform.
57 * The logical content is a sequence of byte values, starting with a Kind.ordinal value.
58 * The sequence is unterminated, ending with an indefinite number of zero bytes.
59 * Sequences that are simple (short enough and with small enough values) pack into a 64-bit long.
60 */
61 private static final class Transform {
62 final long packedBytes;
63 final byte[] fullBytes;
64 final LambdaForm result; // result of transform, or null, if there is none available
65
66 private enum Kind {
67 NO_KIND, // necessary because ordinal must be greater than zero
68 BIND_ARG, ADD_ARG, DUP_ARG,
69 SPREAD_ARGS,
70 FILTER_ARG, FILTER_RETURN, FILTER_RETURN_TO_ZERO,
71 COLLECT_ARGS, COLLECT_ARGS_TO_VOID, COLLECT_ARGS_TO_ARRAY,
72 FOLD_ARGS, FOLD_ARGS_TO_VOID,
73 PERMUTE_ARGS
74 //maybe add more for guard with test, catch exception, pointwise type conversions
75 }
76
77 private static final boolean STRESS_TEST = false; // turn on to disable most packing
78 private static final int
79 PACKED_BYTE_SIZE = (STRESS_TEST ? 2 : 4),
80 PACKED_BYTE_MASK = (1 << PACKED_BYTE_SIZE) - 1,
81 PACKED_BYTE_MAX_LENGTH = (STRESS_TEST ? 3 : 64 / PACKED_BYTE_SIZE);
82
83 private static long packedBytes(byte[] bytes) {
84 if (bytes.length > PACKED_BYTE_MAX_LENGTH) return 0;
123 }
124 assert(packedBytes(bytes) == 0);
125 return bytes;
126 }
127
128 private byte byteAt(int i) {
129 long pb = packedBytes;
130 if (pb == 0) {
131 if (i >= fullBytes.length) return 0;
132 return fullBytes[i];
133 }
134 assert(fullBytes == null);
135 if (i > PACKED_BYTE_MAX_LENGTH) return 0;
136 int pos = (i * PACKED_BYTE_SIZE);
137 return (byte)((pb >>> pos) & PACKED_BYTE_MASK);
138 }
139
140 Kind kind() { return Kind.values()[byteAt(0)]; }
141
142 private Transform(long packedBytes, byte[] fullBytes, LambdaForm result) {
143 this.packedBytes = packedBytes;
144 this.fullBytes = fullBytes;
145 this.result = result;
146 }
147 private Transform(long packedBytes) {
148 this(packedBytes, null, null);
149 assert(packedBytes != 0);
150 }
151 private Transform(byte[] fullBytes) {
152 this(0, fullBytes, null);
153 }
154
155 private static byte bval(int b) {
156 assert((b & 0xFF) == b); // incoming value must fit in *unsigned* byte
157 return (byte)b;
158 }
159 private static byte bval(Kind k) {
160 return bval(k.ordinal());
161 }
162 static Transform of(Kind k, int b1) {
163 byte b0 = bval(k);
164 if (inRange(b0 | b1))
165 return new Transform(packedBytes(b0, b1));
226 }
227 return Arrays.hashCode(fullBytes);
228 }
229 @Override
230 public String toString() {
231 StringBuilder buf = new StringBuilder();
232 long bits = packedBytes;
233 if (bits != 0) {
234 buf.append("(");
235 while (bits != 0) {
236 buf.append(bits & PACKED_BYTE_MASK);
237 bits >>>= PACKED_BYTE_SIZE;
238 if (bits != 0) buf.append(",");
239 }
240 buf.append(")");
241 }
242 if (fullBytes != null) {
243 buf.append("unpacked");
244 buf.append(Arrays.toString(fullBytes));
245 }
246 if (result != null) {
247 buf.append(" result=");
248 buf.append(result);
249 }
250 return buf.toString();
251 }
252 }
253
254 /** Find a previously cached transform equivalent to the given one, and return its result. */
255 private LambdaForm getInCache(Transform key) {
256 assert(key.result == null);
257 // The transformCache is one of null, Transform, Transform[], or ConcurrentHashMap.
258 Object c = lambdaForm.transformCache;
259 Transform k = null;
260 if (c instanceof ConcurrentHashMap) {
261 @SuppressWarnings("unchecked")
262 ConcurrentHashMap<Transform,Transform> m = (ConcurrentHashMap<Transform,Transform>) c;
263 k = m.get(key);
264 } else if (c == null) {
265 return null;
266 } else if (c instanceof Transform) {
267 // one-element cache avoids overhead of an array
268 Transform t = (Transform)c;
269 if (t.equals(key)) k = t;
270 } else {
271 Transform[] ta = (Transform[])c;
272 for (int i = 0; i < ta.length; i++) {
273 Transform t = ta[i];
274 if (t == null) break;
275 if (t.equals(key)) { k = t; break; }
276 }
277 }
278 assert(k == null || key.equals(k));
279 return k == null ? null : k.result;
280 }
281
282 /** Arbitrary but reasonable limits on Transform[] size for cache. */
283 private static final int MIN_CACHE_ARRAY_SIZE = 4, MAX_CACHE_ARRAY_SIZE = 16;
284
285 /** Cache a transform with its result, and return that result.
286 * But if an equivalent transform has already been cached, return its result instead.
287 */
288 private LambdaForm putInCache(Transform key, LambdaForm form) {
289 key = key.withResult(form);
290 for (int pass = 0; ; pass++) {
291 Object c = lambdaForm.transformCache;
292 if (c instanceof ConcurrentHashMap) {
293 @SuppressWarnings("unchecked")
294 ConcurrentHashMap<Transform,Transform> m = (ConcurrentHashMap<Transform,Transform>) c;
295 Transform k = m.putIfAbsent(key, key);
296 return k != null ? k.result : form;
297 }
298 assert(pass == 0);
299 synchronized (lambdaForm) {
300 c = lambdaForm.transformCache;
301 if (c instanceof ConcurrentHashMap)
302 continue;
303 if (c == null) {
304 lambdaForm.transformCache = key;
305 return form;
306 }
307 Transform[] ta;
308 if (c instanceof Transform) {
309 Transform k = (Transform)c;
310 if (k.equals(key)) {
311 return k.result;
312 }
313 // expand one-element cache to small array
314 ta = new Transform[MIN_CACHE_ARRAY_SIZE];
315 ta[0] = k;
316 lambdaForm.transformCache = c = ta;
317 } else {
318 // it is already expanded
319 ta = (Transform[])c;
320 }
321 int len = ta.length;
322 int i;
323 for (i = 0; i < len; i++) {
324 Transform k = ta[i];
325 if (k == null) {
326 break;
327 }
328 if (k.equals(key)) {
329 return k.result;
330 }
331 }
332 if (i < len) {
333 // just fall through to cache update
334 } else if (len < MAX_CACHE_ARRAY_SIZE) {
335 len = Math.min(len * 2, MAX_CACHE_ARRAY_SIZE);
336 ta = Arrays.copyOf(ta, len);
337 lambdaForm.transformCache = ta;
338 } else {
339 ConcurrentHashMap<Transform, Transform> m = new ConcurrentHashMap<>(MAX_CACHE_ARRAY_SIZE * 2);
340 for (Transform k : ta) {
341 m.put(k, k);
342 }
343 lambdaForm.transformCache = m;
344 // The second iteration will update for this query, concurrently.
345 continue;
346 }
347 ta[i] = key;
348 return form;
349 }
350 }
351 }
352
353 private LambdaFormBuffer buffer() {
354 return new LambdaFormBuffer(lambdaForm);
355 }
356
357 /// Editing methods for method handles. These need to have fast paths.
358
359 private BoundMethodHandle.SpeciesData oldSpeciesData() {
360 return BoundMethodHandle.speciesData(lambdaForm);
361 }
362 private BoundMethodHandle.SpeciesData newSpeciesData(BasicType type) {
363 return oldSpeciesData().extendWith(type);
364 }
365
366 BoundMethodHandle bindArgumentL(BoundMethodHandle mh, int pos, Object value) {
367 assert(mh.speciesData() == oldSpeciesData());
|
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.lang.invoke;
27
28 import java.lang.ref.SoftReference;
29 import java.util.Arrays;
30 import static java.lang.invoke.LambdaForm.*;
31 import static java.lang.invoke.LambdaForm.BasicType.*;
32 import static java.lang.invoke.MethodHandleImpl.Intrinsic;
33 import java.util.Collections;
34 import java.util.concurrent.ConcurrentHashMap;
35
36 import sun.invoke.util.Wrapper;
37
38 /** Transforms on LFs.
39 * A lambda-form editor can derive new LFs from its base LF.
40 * The editor can cache derived LFs, which simplifies the reuse of their underlying bytecodes.
41 * To support this caching, a LF has an optional pointer to its editor.
42 */
43 class LambdaFormEditor {
44 final LambdaForm lambdaForm;
45
46 private LambdaFormEditor(LambdaForm lambdaForm) {
47 this.lambdaForm = lambdaForm;
48 }
49
50 // Factory method.
51 static LambdaFormEditor lambdaFormEditor(LambdaForm lambdaForm) {
52 // TO DO: Consider placing intern logic here, to cut down on duplication.
53 // lambdaForm = findPreexistingEquivalent(lambdaForm)
54 return new LambdaFormEditor(lambdaForm);
55 }
56
57 /** A description of a cached transform, possibly associated with the result of the transform.
58 * The logical content is a sequence of byte values, starting with a Kind.ordinal value.
59 * The sequence is unterminated, ending with an indefinite number of zero bytes.
60 * Sequences that are simple (short enough and with small enough values) pack into a 64-bit long.
61 */
62 private static final class Transform extends SoftReference<LambdaForm> {
63 final long packedBytes;
64 final byte[] fullBytes;
65
66 private enum Kind {
67 NO_KIND, // necessary because ordinal must be greater than zero
68 BIND_ARG, ADD_ARG, DUP_ARG,
69 SPREAD_ARGS,
70 FILTER_ARG, FILTER_RETURN, FILTER_RETURN_TO_ZERO,
71 COLLECT_ARGS, COLLECT_ARGS_TO_VOID, COLLECT_ARGS_TO_ARRAY,
72 FOLD_ARGS, FOLD_ARGS_TO_VOID,
73 PERMUTE_ARGS
74 //maybe add more for guard with test, catch exception, pointwise type conversions
75 }
76
77 private static final boolean STRESS_TEST = false; // turn on to disable most packing
78 private static final int
79 PACKED_BYTE_SIZE = (STRESS_TEST ? 2 : 4),
80 PACKED_BYTE_MASK = (1 << PACKED_BYTE_SIZE) - 1,
81 PACKED_BYTE_MAX_LENGTH = (STRESS_TEST ? 3 : 64 / PACKED_BYTE_SIZE);
82
83 private static long packedBytes(byte[] bytes) {
84 if (bytes.length > PACKED_BYTE_MAX_LENGTH) return 0;
123 }
124 assert(packedBytes(bytes) == 0);
125 return bytes;
126 }
127
128 private byte byteAt(int i) {
129 long pb = packedBytes;
130 if (pb == 0) {
131 if (i >= fullBytes.length) return 0;
132 return fullBytes[i];
133 }
134 assert(fullBytes == null);
135 if (i > PACKED_BYTE_MAX_LENGTH) return 0;
136 int pos = (i * PACKED_BYTE_SIZE);
137 return (byte)((pb >>> pos) & PACKED_BYTE_MASK);
138 }
139
140 Kind kind() { return Kind.values()[byteAt(0)]; }
141
142 private Transform(long packedBytes, byte[] fullBytes, LambdaForm result) {
143 super(result);
144 this.packedBytes = packedBytes;
145 this.fullBytes = fullBytes;
146 }
147 private Transform(long packedBytes) {
148 this(packedBytes, null, null);
149 assert(packedBytes != 0);
150 }
151 private Transform(byte[] fullBytes) {
152 this(0, fullBytes, null);
153 }
154
155 private static byte bval(int b) {
156 assert((b & 0xFF) == b); // incoming value must fit in *unsigned* byte
157 return (byte)b;
158 }
159 private static byte bval(Kind k) {
160 return bval(k.ordinal());
161 }
162 static Transform of(Kind k, int b1) {
163 byte b0 = bval(k);
164 if (inRange(b0 | b1))
165 return new Transform(packedBytes(b0, b1));
226 }
227 return Arrays.hashCode(fullBytes);
228 }
229 @Override
230 public String toString() {
231 StringBuilder buf = new StringBuilder();
232 long bits = packedBytes;
233 if (bits != 0) {
234 buf.append("(");
235 while (bits != 0) {
236 buf.append(bits & PACKED_BYTE_MASK);
237 bits >>>= PACKED_BYTE_SIZE;
238 if (bits != 0) buf.append(",");
239 }
240 buf.append(")");
241 }
242 if (fullBytes != null) {
243 buf.append("unpacked");
244 buf.append(Arrays.toString(fullBytes));
245 }
246 LambdaForm result = get();
247 if (result != null) {
248 buf.append(" result=");
249 buf.append(result);
250 }
251 return buf.toString();
252 }
253 }
254
255 /** Find a previously cached transform equivalent to the given one, and return its result. */
256 private LambdaForm getInCache(Transform key) {
257 assert(key.get() == null);
258 // The transformCache is one of null, Transform, Transform[], or ConcurrentHashMap.
259 Object c = lambdaForm.transformCache;
260 Transform k = null;
261 if (c instanceof ConcurrentHashMap) {
262 @SuppressWarnings("unchecked")
263 ConcurrentHashMap<Transform,Transform> m = (ConcurrentHashMap<Transform,Transform>) c;
264 k = m.get(key);
265 } else if (c == null) {
266 return null;
267 } else if (c instanceof Transform) {
268 // one-element cache avoids overhead of an array
269 Transform t = (Transform)c;
270 if (t.equals(key)) k = t;
271 } else {
272 Transform[] ta = (Transform[])c;
273 for (int i = 0; i < ta.length; i++) {
274 Transform t = ta[i];
275 if (t == null) break;
276 if (t.equals(key)) { k = t; break; }
277 }
278 }
279 assert(k == null || key.equals(k));
280 return (k != null) ? k.get() : null;
281 }
282
283 /** Arbitrary but reasonable limits on Transform[] size for cache. */
284 private static final int MIN_CACHE_ARRAY_SIZE = 4, MAX_CACHE_ARRAY_SIZE = 16;
285
286 /** Cache a transform with its result, and return that result.
287 * But if an equivalent transform has already been cached, return its result instead.
288 */
289 private LambdaForm putInCache(Transform key, LambdaForm form) {
290 key = key.withResult(form);
291 for (int pass = 0; ; pass++) {
292 Object c = lambdaForm.transformCache;
293 if (c instanceof ConcurrentHashMap) {
294 @SuppressWarnings("unchecked")
295 ConcurrentHashMap<Transform,Transform> m = (ConcurrentHashMap<Transform,Transform>) c;
296 Transform k = m.putIfAbsent(key, key);
297 if (k == null) return form;
298 LambdaForm result = k.get();
299 if (result != null) {
300 return result;
301 } else {
302 if (m.replace(key, k, key)) {
303 return form;
304 } else {
305 continue;
306 }
307 }
308 }
309 assert(pass == 0);
310 synchronized (lambdaForm) {
311 c = lambdaForm.transformCache;
312 if (c instanceof ConcurrentHashMap)
313 continue;
314 if (c == null) {
315 lambdaForm.transformCache = key;
316 return form;
317 }
318 Transform[] ta;
319 if (c instanceof Transform) {
320 Transform k = (Transform)c;
321 if (k.equals(key)) {
322 LambdaForm result = k.get();
323 if (result == null) {
324 lambdaForm.transformCache = key;
325 return form;
326 } else {
327 return result;
328 }
329 } else if (k.get() == null) { // overwrite stale entry
330 lambdaForm.transformCache = key;
331 return form;
332 }
333 // expand one-element cache to small array
334 ta = new Transform[MIN_CACHE_ARRAY_SIZE];
335 ta[0] = k;
336 lambdaForm.transformCache = ta;
337 } else {
338 // it is already expanded
339 ta = (Transform[])c;
340 }
341 int len = ta.length;
342 int stale = -1;
343 int i;
344 for (i = 0; i < len; i++) {
345 Transform k = ta[i];
346 if (k == null) {
347 break;
348 }
349 if (k.equals(key)) {
350 LambdaForm result = k.get();
351 if (result == null) {
352 ta[i] = key;
353 return form;
354 } else {
355 return result;
356 }
357 } else if (stale < 0 && k.get() == null) {
358 stale = i; // remember 1st stale entry index
359 }
360 }
361 if (i < len || stale >= 0) {
362 // just fall through to cache update
363 } else if (len < MAX_CACHE_ARRAY_SIZE) {
364 len = Math.min(len * 2, MAX_CACHE_ARRAY_SIZE);
365 ta = Arrays.copyOf(ta, len);
366 lambdaForm.transformCache = ta;
367 } else {
368 ConcurrentHashMap<Transform, Transform> m = new ConcurrentHashMap<>(MAX_CACHE_ARRAY_SIZE * 2);
369 for (Transform k : ta) {
370 m.put(k, k);
371 }
372 lambdaForm.transformCache = m;
373 // The second iteration will update for this query, concurrently.
374 continue;
375 }
376 int idx = (stale >= 0) ? stale : i;
377 ta[idx] = key;
378 return form;
379 }
380 }
381 }
382
383 private LambdaFormBuffer buffer() {
384 return new LambdaFormBuffer(lambdaForm);
385 }
386
387 /// Editing methods for method handles. These need to have fast paths.
388
389 private BoundMethodHandle.SpeciesData oldSpeciesData() {
390 return BoundMethodHandle.speciesData(lambdaForm);
391 }
392 private BoundMethodHandle.SpeciesData newSpeciesData(BasicType type) {
393 return oldSpeciesData().extendWith(type);
394 }
395
396 BoundMethodHandle bindArgumentL(BoundMethodHandle mh, int pos, Object value) {
397 assert(mh.speciesData() == oldSpeciesData());
|