author | redestad |
Wed, 17 Apr 2019 00:06:38 +0200 | |
changeset 54550 | 5fa7fbddfe9d |
parent 52497 | 34510f65fb58 |
child 54652 | 1cb0306f16d1 |
permissions | -rw-r--r-- |
35388 | 1 |
/* |
54550
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
2 |
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. |
35388 | 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. Oracle designates this |
|
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 |
||
46902
c6cefe631b18
8186500: StringConcatFactory.makeConcatWithConstants throws AssertionError when recipe contains non-String constants
redestad
parents:
42105
diff
changeset
|
28 |
import jdk.internal.misc.Unsafe; |
54550
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
29 |
import jdk.internal.misc.VM; |
35388 | 30 |
import jdk.internal.org.objectweb.asm.ClassWriter; |
31 |
import jdk.internal.org.objectweb.asm.Label; |
|
32 |
import jdk.internal.org.objectweb.asm.MethodVisitor; |
|
33 |
import jdk.internal.org.objectweb.asm.Opcodes; |
|
34 |
import jdk.internal.vm.annotation.ForceInline; |
|
46902
c6cefe631b18
8186500: StringConcatFactory.makeConcatWithConstants throws AssertionError when recipe contains non-String constants
redestad
parents:
42105
diff
changeset
|
35 |
import sun.invoke.util.Wrapper; |
c6cefe631b18
8186500: StringConcatFactory.makeConcatWithConstants throws AssertionError when recipe contains non-String constants
redestad
parents:
42105
diff
changeset
|
36 |
import sun.security.action.GetPropertyAction; |
35388 | 37 |
|
38 |
import java.lang.invoke.MethodHandles.Lookup; |
|
46902
c6cefe631b18
8186500: StringConcatFactory.makeConcatWithConstants throws AssertionError when recipe contains non-String constants
redestad
parents:
42105
diff
changeset
|
39 |
import java.util.ArrayList; |
c6cefe631b18
8186500: StringConcatFactory.makeConcatWithConstants throws AssertionError when recipe contains non-String constants
redestad
parents:
42105
diff
changeset
|
40 |
import java.util.Arrays; |
c6cefe631b18
8186500: StringConcatFactory.makeConcatWithConstants throws AssertionError when recipe contains non-String constants
redestad
parents:
42105
diff
changeset
|
41 |
import java.util.List; |
c6cefe631b18
8186500: StringConcatFactory.makeConcatWithConstants throws AssertionError when recipe contains non-String constants
redestad
parents:
42105
diff
changeset
|
42 |
import java.util.Objects; |
c6cefe631b18
8186500: StringConcatFactory.makeConcatWithConstants throws AssertionError when recipe contains non-String constants
redestad
parents:
42105
diff
changeset
|
43 |
import java.util.Properties; |
35388 | 44 |
import java.util.concurrent.ConcurrentHashMap; |
45 |
import java.util.concurrent.ConcurrentMap; |
|
46 |
import java.util.function.Function; |
|
47 |
||
48 |
import static jdk.internal.org.objectweb.asm.Opcodes.*; |
|
49 |
||
50 |
/** |
|
51 |
* <p>Methods to facilitate the creation of String concatenation methods, that |
|
52 |
* can be used to efficiently concatenate a known number of arguments of known |
|
53 |
* types, possibly after type adaptation and partial evaluation of arguments. |
|
54 |
* These methods are typically used as <em>bootstrap methods</em> for {@code |
|
55 |
* invokedynamic} call sites, to support the <em>string concatenation</em> |
|
56 |
* feature of the Java Programming Language. |
|
57 |
* |
|
58 |
* <p>Indirect access to the behavior specified by the provided {@code |
|
59 |
* MethodHandle} proceeds in order through two phases: |
|
60 |
* |
|
61 |
* <ol> |
|
62 |
* <li><em>Linkage</em> occurs when the methods in this class are invoked. |
|
63 |
* They take as arguments a method type describing the concatenated arguments |
|
64 |
* count and types, and optionally the String <em>recipe</em>, plus the |
|
65 |
* constants that participate in the String concatenation. The details on |
|
66 |
* accepted recipe shapes are described further below. Linkage may involve |
|
67 |
* dynamically loading a new class that implements the expected concatenation |
|
68 |
* behavior. The {@code CallSite} holds the {@code MethodHandle} pointing to the |
|
69 |
* exact concatenation method. The concatenation methods may be shared among |
|
70 |
* different {@code CallSite}s, e.g. if linkage methods produce them as pure |
|
71 |
* functions.</li> |
|
72 |
* |
|
73 |
* <li><em>Invocation</em> occurs when a generated concatenation method is |
|
74 |
* invoked with the exact dynamic arguments. This may occur many times for a |
|
75 |
* single concatenation method. The method referenced by the behavior {@code |
|
76 |
* MethodHandle} is invoked with the static arguments and any additional dynamic |
|
77 |
* arguments provided on invocation, as if by {@link MethodHandle#invoke(Object...)}.</li> |
|
78 |
* </ol> |
|
79 |
* |
|
80 |
* <p> This class provides two forms of linkage methods: a simple version |
|
81 |
* ({@link #makeConcat(java.lang.invoke.MethodHandles.Lookup, String, |
|
82 |
* MethodType)}) using only the dynamic arguments, and an advanced version |
|
83 |
* ({@link #makeConcatWithConstants(java.lang.invoke.MethodHandles.Lookup, |
|
84 |
* String, MethodType, String, Object...)} using the advanced forms of capturing |
|
85 |
* the constant arguments. The advanced strategy can produce marginally better |
|
86 |
* invocation bytecode, at the expense of exploding the number of shapes of |
|
87 |
* string concatenation methods present at runtime, because those shapes would |
|
88 |
* include constant static arguments as well. |
|
89 |
* |
|
90 |
* @author Aleksey Shipilev |
|
91 |
* @author Remi Forax |
|
92 |
* @author Peter Levart |
|
93 |
* |
|
94 |
* @apiNote |
|
95 |
* <p>There is a JVM limit (classfile structural constraint): no method |
|
96 |
* can call with more than 255 slots. This limits the number of static and |
|
97 |
* dynamic arguments one can pass to bootstrap method. Since there are potential |
|
98 |
* concatenation strategies that use {@code MethodHandle} combinators, we need |
|
36441
f40a48b83f57
8151063: Typo in java.lang.invoke.StringConcatFactory javadoc
rriggs
parents:
36001
diff
changeset
|
99 |
* to reserve a few empty slots on the parameter lists to capture the |
35388 | 100 |
* temporal results. This is why bootstrap methods in this factory do not accept |
101 |
* more than 200 argument slots. Users requiring more than 200 argument slots in |
|
102 |
* concatenation are expected to split the large concatenation in smaller |
|
103 |
* expressions. |
|
35401 | 104 |
* |
105 |
* @since 9 |
|
35388 | 106 |
*/ |
107 |
public final class StringConcatFactory { |
|
108 |
||
109 |
/** |
|
110 |
* Tag used to demarcate an ordinary argument. |
|
111 |
*/ |
|
112 |
private static final char TAG_ARG = '\u0001'; |
|
113 |
||
114 |
/** |
|
115 |
* Tag used to demarcate a constant. |
|
116 |
*/ |
|
117 |
private static final char TAG_CONST = '\u0002'; |
|
118 |
||
119 |
/** |
|
120 |
* Maximum number of argument slots in String Concat call. |
|
121 |
* |
|
122 |
* While the maximum number of argument slots that indy call can handle is 253, |
|
123 |
* we do not use all those slots, to let the strategies with MethodHandle |
|
124 |
* combinators to use some arguments. |
|
125 |
*/ |
|
126 |
private static final int MAX_INDY_CONCAT_ARG_SLOTS = 200; |
|
127 |
||
128 |
/** |
|
129 |
* Concatenation strategy to use. See {@link Strategy} for possible options. |
|
130 |
* This option is controllable with -Djava.lang.invoke.stringConcat JDK option. |
|
131 |
*/ |
|
37684
38b7cb606a05
8155090: String concatenation fails with a custom SecurityManager that uses concatenation
shade
parents:
37593
diff
changeset
|
132 |
private static Strategy STRATEGY; |
35388 | 133 |
|
134 |
/** |
|
135 |
* Default strategy to use for concatenation. |
|
136 |
*/ |
|
38428
0079e7d659db
8148604: JEP 280, Switch to more optimal concatenation strategy
shade
parents:
37781
diff
changeset
|
137 |
private static final Strategy DEFAULT_STRATEGY = Strategy.MH_INLINE_SIZED_EXACT; |
35388 | 138 |
|
139 |
private enum Strategy { |
|
140 |
/** |
|
141 |
* Bytecode generator, calling into {@link java.lang.StringBuilder}. |
|
142 |
*/ |
|
143 |
BC_SB, |
|
144 |
||
145 |
/** |
|
146 |
* Bytecode generator, calling into {@link java.lang.StringBuilder}; |
|
147 |
* but trying to estimate the required storage. |
|
148 |
*/ |
|
149 |
BC_SB_SIZED, |
|
150 |
||
151 |
/** |
|
152 |
* Bytecode generator, calling into {@link java.lang.StringBuilder}; |
|
153 |
* but computing the required storage exactly. |
|
154 |
*/ |
|
155 |
BC_SB_SIZED_EXACT, |
|
156 |
||
157 |
/** |
|
158 |
* MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}. |
|
159 |
* This strategy also tries to estimate the required storage. |
|
160 |
*/ |
|
161 |
MH_SB_SIZED, |
|
162 |
||
163 |
/** |
|
164 |
* MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}. |
|
165 |
* This strategy also estimate the required storage exactly. |
|
166 |
*/ |
|
167 |
MH_SB_SIZED_EXACT, |
|
168 |
||
169 |
/** |
|
170 |
* MethodHandle-based generator, that constructs its own byte[] array from |
|
171 |
* the arguments. It computes the required storage exactly. |
|
172 |
*/ |
|
173 |
MH_INLINE_SIZED_EXACT |
|
174 |
} |
|
175 |
||
176 |
/** |
|
177 |
* Enables debugging: this may print debugging messages, perform additional (non-neutral for performance) |
|
178 |
* checks, etc. |
|
179 |
*/ |
|
180 |
private static final boolean DEBUG; |
|
181 |
||
182 |
/** |
|
183 |
* Enables caching of strategy stubs. This may improve the linkage time by reusing the generated |
|
184 |
* code, at the expense of contaminating the profiles. |
|
185 |
*/ |
|
186 |
private static final boolean CACHE_ENABLE; |
|
187 |
||
188 |
private static final ConcurrentMap<Key, MethodHandle> CACHE; |
|
189 |
||
35776
dd5df83fcc0d
8149459: StringConcatFactory should be synced up with LambdaMetafactory
shade
parents:
35700
diff
changeset
|
190 |
/** |
dd5df83fcc0d
8149459: StringConcatFactory should be synced up with LambdaMetafactory
shade
parents:
35700
diff
changeset
|
191 |
* Dump generated classes to disk, for debugging purposes. |
dd5df83fcc0d
8149459: StringConcatFactory should be synced up with LambdaMetafactory
shade
parents:
35700
diff
changeset
|
192 |
*/ |
dd5df83fcc0d
8149459: StringConcatFactory should be synced up with LambdaMetafactory
shade
parents:
35700
diff
changeset
|
193 |
private static final ProxyClassesDumper DUMPER; |
dd5df83fcc0d
8149459: StringConcatFactory should be synced up with LambdaMetafactory
shade
parents:
35700
diff
changeset
|
194 |
|
54550
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
195 |
private static final Class<?> STRING_HELPER; |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
196 |
|
35388 | 197 |
static { |
37684
38b7cb606a05
8155090: String concatenation fails with a custom SecurityManager that uses concatenation
shade
parents:
37593
diff
changeset
|
198 |
// In case we need to double-back onto the StringConcatFactory during this |
38b7cb606a05
8155090: String concatenation fails with a custom SecurityManager that uses concatenation
shade
parents:
37593
diff
changeset
|
199 |
// static initialization, make sure we have the reasonable defaults to complete |
47478 | 200 |
// the static initialization properly. After that, actual users would use |
47471 | 201 |
// the proper values we have read from the properties. |
37684
38b7cb606a05
8155090: String concatenation fails with a custom SecurityManager that uses concatenation
shade
parents:
37593
diff
changeset
|
202 |
STRATEGY = DEFAULT_STRATEGY; |
38b7cb606a05
8155090: String concatenation fails with a custom SecurityManager that uses concatenation
shade
parents:
37593
diff
changeset
|
203 |
// CACHE_ENABLE = false; // implied |
38b7cb606a05
8155090: String concatenation fails with a custom SecurityManager that uses concatenation
shade
parents:
37593
diff
changeset
|
204 |
// CACHE = null; // implied |
38b7cb606a05
8155090: String concatenation fails with a custom SecurityManager that uses concatenation
shade
parents:
37593
diff
changeset
|
205 |
// DEBUG = false; // implied |
38b7cb606a05
8155090: String concatenation fails with a custom SecurityManager that uses concatenation
shade
parents:
37593
diff
changeset
|
206 |
// DUMPER = null; // implied |
38b7cb606a05
8155090: String concatenation fails with a custom SecurityManager that uses concatenation
shade
parents:
37593
diff
changeset
|
207 |
|
54550
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
208 |
try { |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
209 |
STRING_HELPER = Class.forName("java.lang.StringConcatHelper"); |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
210 |
} catch (Throwable e) { |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
211 |
throw new AssertionError(e); |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
212 |
} |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
213 |
|
37593
824750ada3d6
8154231: Simplify access to System properties from JDK code
redestad
parents:
37332
diff
changeset
|
214 |
final String strategy = |
54550
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
215 |
VM.getSavedProperty("java.lang.invoke.stringConcat"); |
37593
824750ada3d6
8154231: Simplify access to System properties from JDK code
redestad
parents:
37332
diff
changeset
|
216 |
CACHE_ENABLE = Boolean.parseBoolean( |
54550
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
217 |
VM.getSavedProperty("java.lang.invoke.stringConcat.cache")); |
37593
824750ada3d6
8154231: Simplify access to System properties from JDK code
redestad
parents:
37332
diff
changeset
|
218 |
DEBUG = Boolean.parseBoolean( |
54550
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
219 |
VM.getSavedProperty("java.lang.invoke.stringConcat.debug")); |
37593
824750ada3d6
8154231: Simplify access to System properties from JDK code
redestad
parents:
37332
diff
changeset
|
220 |
final String dumpPath = |
54550
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
221 |
VM.getSavedProperty("java.lang.invoke.stringConcat.dumpClasses"); |
35388 | 222 |
|
223 |
STRATEGY = (strategy == null) ? DEFAULT_STRATEGY : Strategy.valueOf(strategy); |
|
224 |
CACHE = CACHE_ENABLE ? new ConcurrentHashMap<>() : null; |
|
35776
dd5df83fcc0d
8149459: StringConcatFactory should be synced up with LambdaMetafactory
shade
parents:
35700
diff
changeset
|
225 |
DUMPER = (dumpPath == null) ? null : ProxyClassesDumper.getInstance(dumpPath); |
35388 | 226 |
} |
227 |
||
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
228 |
/** |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
229 |
* Cache key is a composite of: |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
230 |
* - class name, that lets to disambiguate stubs, to avoid excess sharing |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
231 |
* - method type, describing the dynamic arguments for concatenation |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
232 |
* - concat recipe, describing the constants and concat shape |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
233 |
*/ |
35388 | 234 |
private static final class Key { |
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
235 |
final String className; |
35388 | 236 |
final MethodType mt; |
237 |
final Recipe recipe; |
|
238 |
||
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
239 |
public Key(String className, MethodType mt, Recipe recipe) { |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
240 |
this.className = className; |
35388 | 241 |
this.mt = mt; |
242 |
this.recipe = recipe; |
|
243 |
} |
|
244 |
||
245 |
@Override |
|
246 |
public boolean equals(Object o) { |
|
247 |
if (this == o) return true; |
|
248 |
if (o == null || getClass() != o.getClass()) return false; |
|
249 |
||
250 |
Key key = (Key) o; |
|
251 |
||
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
252 |
if (!className.equals(key.className)) return false; |
35388 | 253 |
if (!mt.equals(key.mt)) return false; |
254 |
if (!recipe.equals(key.recipe)) return false; |
|
255 |
return true; |
|
256 |
} |
|
257 |
||
258 |
@Override |
|
259 |
public int hashCode() { |
|
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
260 |
int result = className.hashCode(); |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
261 |
result = 31 * result + mt.hashCode(); |
35388 | 262 |
result = 31 * result + recipe.hashCode(); |
263 |
return result; |
|
264 |
} |
|
265 |
} |
|
266 |
||
267 |
/** |
|
268 |
* Parses the recipe string, and produces the traversable collection of |
|
269 |
* {@link java.lang.invoke.StringConcatFactory.RecipeElement}-s for generator |
|
270 |
* strategies. Notably, this class parses out the constants from the recipe |
|
271 |
* and from other static arguments. |
|
272 |
*/ |
|
273 |
private static final class Recipe { |
|
274 |
private final List<RecipeElement> elements; |
|
275 |
||
276 |
public Recipe(String src, Object[] constants) { |
|
277 |
List<RecipeElement> el = new ArrayList<>(); |
|
278 |
||
279 |
int constC = 0; |
|
280 |
int argC = 0; |
|
281 |
||
282 |
StringBuilder acc = new StringBuilder(); |
|
283 |
||
284 |
for (int i = 0; i < src.length(); i++) { |
|
285 |
char c = src.charAt(i); |
|
286 |
||
287 |
if (c == TAG_CONST || c == TAG_ARG) { |
|
288 |
// Detected a special tag, flush all accumulated characters |
|
289 |
// as a constant first: |
|
290 |
if (acc.length() > 0) { |
|
291 |
el.add(new RecipeElement(acc.toString())); |
|
292 |
acc.setLength(0); |
|
293 |
} |
|
294 |
if (c == TAG_CONST) { |
|
295 |
Object cnst = constants[constC++]; |
|
296 |
el.add(new RecipeElement(cnst)); |
|
40256
c5e03eaf7ba2
8163370: Reduce number of classes loaded by common usage of java.lang.invoke
redestad
parents:
38868
diff
changeset
|
297 |
} else if (c == TAG_ARG) { |
35388 | 298 |
el.add(new RecipeElement(argC++)); |
299 |
} |
|
300 |
} else { |
|
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
301 |
// Not a special character, this is a constant embedded into |
35388 | 302 |
// the recipe itself. |
303 |
acc.append(c); |
|
304 |
} |
|
305 |
} |
|
306 |
||
307 |
// Flush the remaining characters as constant: |
|
308 |
if (acc.length() > 0) { |
|
309 |
el.add(new RecipeElement(acc.toString())); |
|
310 |
} |
|
311 |
||
36737
67b2804be5ab
8152951: Avoid calculating the reverse of StringConcatFactory$Recipe elements
redestad
parents:
36668
diff
changeset
|
312 |
elements = el; |
35388 | 313 |
} |
314 |
||
36737
67b2804be5ab
8152951: Avoid calculating the reverse of StringConcatFactory$Recipe elements
redestad
parents:
36668
diff
changeset
|
315 |
public List<RecipeElement> getElements() { |
35388 | 316 |
return elements; |
317 |
} |
|
318 |
||
319 |
@Override |
|
320 |
public boolean equals(Object o) { |
|
321 |
if (this == o) return true; |
|
322 |
if (o == null || getClass() != o.getClass()) return false; |
|
323 |
||
324 |
Recipe recipe = (Recipe) o; |
|
325 |
return elements.equals(recipe.elements); |
|
326 |
} |
|
327 |
||
328 |
@Override |
|
52326
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
329 |
public String toString() { |
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
330 |
return "Recipe{" + |
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
331 |
"elements=" + elements + |
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
332 |
'}'; |
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
333 |
} |
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
334 |
|
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
335 |
@Override |
35388 | 336 |
public int hashCode() { |
337 |
return elements.hashCode(); |
|
338 |
} |
|
339 |
} |
|
340 |
||
341 |
private static final class RecipeElement { |
|
46902
c6cefe631b18
8186500: StringConcatFactory.makeConcatWithConstants throws AssertionError when recipe contains non-String constants
redestad
parents:
42105
diff
changeset
|
342 |
private final String value; |
35388 | 343 |
private final int argPos; |
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
344 |
private final char tag; |
35388 | 345 |
|
346 |
public RecipeElement(Object cnst) { |
|
46902
c6cefe631b18
8186500: StringConcatFactory.makeConcatWithConstants throws AssertionError when recipe contains non-String constants
redestad
parents:
42105
diff
changeset
|
347 |
this.value = String.valueOf(Objects.requireNonNull(cnst)); |
35388 | 348 |
this.argPos = -1; |
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
349 |
this.tag = TAG_CONST; |
35388 | 350 |
} |
351 |
||
352 |
public RecipeElement(int arg) { |
|
353 |
this.value = null; |
|
354 |
this.argPos = arg; |
|
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
355 |
this.tag = TAG_ARG; |
35388 | 356 |
} |
357 |
||
46902
c6cefe631b18
8186500: StringConcatFactory.makeConcatWithConstants throws AssertionError when recipe contains non-String constants
redestad
parents:
42105
diff
changeset
|
358 |
public String getValue() { |
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
359 |
assert (tag == TAG_CONST); |
35388 | 360 |
return value; |
361 |
} |
|
362 |
||
363 |
public int getArgPos() { |
|
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
364 |
assert (tag == TAG_ARG); |
35388 | 365 |
return argPos; |
366 |
} |
|
367 |
||
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
368 |
public char getTag() { |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
369 |
return tag; |
35388 | 370 |
} |
371 |
||
372 |
@Override |
|
373 |
public boolean equals(Object o) { |
|
374 |
if (this == o) return true; |
|
375 |
if (o == null || getClass() != o.getClass()) return false; |
|
376 |
||
377 |
RecipeElement that = (RecipeElement) o; |
|
378 |
||
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
379 |
if (this.tag != that.tag) return false; |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
380 |
if (this.tag == TAG_CONST && (!value.equals(that.value))) return false; |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
381 |
if (this.tag == TAG_ARG && (argPos != that.argPos)) return false; |
35388 | 382 |
return true; |
383 |
} |
|
384 |
||
385 |
@Override |
|
52326
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
386 |
public String toString() { |
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
387 |
return "RecipeElement{" + |
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
388 |
"value='" + value + '\'' + |
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
389 |
", argPos=" + argPos + |
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
390 |
", tag=" + tag + |
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
391 |
'}'; |
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
392 |
} |
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
393 |
|
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
394 |
@Override |
35388 | 395 |
public int hashCode() { |
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
396 |
return (int)tag; |
35388 | 397 |
} |
398 |
} |
|
399 |
||
48930
b1a5b4ad7427
8198523: Refactor BootstrapMethodInvoker to further avoid runtime type checks
redestad
parents:
47985
diff
changeset
|
400 |
// StringConcatFactory bootstrap methods are startup sensitive, and may be |
b1a5b4ad7427
8198523: Refactor BootstrapMethodInvoker to further avoid runtime type checks
redestad
parents:
47985
diff
changeset
|
401 |
// special cased in java.lang.invokeBootstrapMethodInvoker to ensure |
b1a5b4ad7427
8198523: Refactor BootstrapMethodInvoker to further avoid runtime type checks
redestad
parents:
47985
diff
changeset
|
402 |
// methods are invoked with exact type information to avoid generating |
b1a5b4ad7427
8198523: Refactor BootstrapMethodInvoker to further avoid runtime type checks
redestad
parents:
47985
diff
changeset
|
403 |
// code for runtime checks. Take care any changes or additions here are |
b1a5b4ad7427
8198523: Refactor BootstrapMethodInvoker to further avoid runtime type checks
redestad
parents:
47985
diff
changeset
|
404 |
// reflected there as appropriate. |
b1a5b4ad7427
8198523: Refactor BootstrapMethodInvoker to further avoid runtime type checks
redestad
parents:
47985
diff
changeset
|
405 |
|
35388 | 406 |
/** |
407 |
* Facilitates the creation of optimized String concatenation methods, that |
|
408 |
* can be used to efficiently concatenate a known number of arguments of |
|
409 |
* known types, possibly after type adaptation and partial evaluation of |
|
410 |
* arguments. Typically used as a <em>bootstrap method</em> for {@code |
|
411 |
* invokedynamic} call sites, to support the <em>string concatenation</em> |
|
412 |
* feature of the Java Programming Language. |
|
413 |
* |
|
414 |
* <p>When the target of the {@code CallSite} returned from this method is |
|
415 |
* invoked, it returns the result of String concatenation, taking all |
|
416 |
* function arguments passed to the linkage method as inputs for |
|
417 |
* concatenation. The target signature is given by {@code concatType}. |
|
47985
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
418 |
* For a target accepting: |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
419 |
* <ul> |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
420 |
* <li>zero inputs, concatenation results in an empty string;</li> |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
421 |
* <li>one input, concatenation results in the single |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
422 |
* input converted as per JLS 5.1.11 "String Conversion"; otherwise</li> |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
423 |
* <li>two or more inputs, the inputs are concatenated as per |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
424 |
* requirements stated in JLS 15.18.1 "String Concatenation Operator +". |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
425 |
* The inputs are converted as per JLS 5.1.11 "String Conversion", |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
426 |
* and combined from left to right.</li> |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
427 |
* </ul> |
35388 | 428 |
* |
429 |
* <p>Assume the linkage arguments are as follows: |
|
430 |
* |
|
431 |
* <ul> |
|
432 |
* <li>{@code concatType}, describing the {@code CallSite} signature</li> |
|
433 |
* </ul> |
|
434 |
* |
|
435 |
* <p>Then the following linkage invariants must hold: |
|
436 |
* |
|
437 |
* <ul> |
|
47461
60a5d9c77c4c
8187089: StringConcatFactory.makeConcat & makeConcatWithConstants should throw StringConcatException if parameter slot count is over 200
mchung
parents:
47216
diff
changeset
|
438 |
* <li>The number of parameter slots in {@code concatType} is |
60a5d9c77c4c
8187089: StringConcatFactory.makeConcat & makeConcatWithConstants should throw StringConcatException if parameter slot count is over 200
mchung
parents:
47216
diff
changeset
|
439 |
* less than or equal to 200</li> |
35388 | 440 |
* <li>The return type in {@code concatType} is assignable from {@link java.lang.String}</li> |
441 |
* </ul> |
|
442 |
* |
|
443 |
* @param lookup Represents a lookup context with the accessibility |
|
47985
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
444 |
* privileges of the caller. Specifically, the lookup |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
445 |
* context must have |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
446 |
* <a href="MethodHandles.Lookup.html#privacc">private access</a> |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
447 |
* privileges. |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
448 |
* When used with {@code invokedynamic}, this is stacked |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
449 |
* automatically by the VM. |
35388 | 450 |
* @param name The name of the method to implement. This name is |
451 |
* arbitrary, and has no meaning for this linkage method. |
|
452 |
* When used with {@code invokedynamic}, this is provided by |
|
453 |
* the {@code NameAndType} of the {@code InvokeDynamic} |
|
454 |
* structure and is stacked automatically by the VM. |
|
455 |
* @param concatType The expected signature of the {@code CallSite}. The |
|
456 |
* parameter types represent the types of concatenation |
|
457 |
* arguments; the return type is always assignable from {@link |
|
458 |
* java.lang.String}. When used with {@code invokedynamic}, |
|
459 |
* this is provided by the {@code NameAndType} of the {@code |
|
460 |
* InvokeDynamic} structure and is stacked automatically by |
|
461 |
* the VM. |
|
462 |
* @return a CallSite whose target can be used to perform String |
|
463 |
* concatenation, with dynamic concatenation arguments described by the given |
|
464 |
* {@code concatType}. |
|
465 |
* @throws StringConcatException If any of the linkage invariants described |
|
47985
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
466 |
* here are violated, or the lookup context |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
467 |
* does not have private access privileges. |
35388 | 468 |
* @throws NullPointerException If any of the incoming arguments is null. |
469 |
* This will never happen when a bootstrap method |
|
470 |
* is called with invokedynamic. |
|
471 |
* |
|
472 |
* @jls 5.1.11 String Conversion |
|
473 |
* @jls 15.18.1 String Concatenation Operator + |
|
474 |
*/ |
|
475 |
public static CallSite makeConcat(MethodHandles.Lookup lookup, |
|
476 |
String name, |
|
477 |
MethodType concatType) throws StringConcatException { |
|
478 |
if (DEBUG) { |
|
479 |
System.out.println("StringConcatFactory " + STRATEGY + " is here for " + concatType); |
|
480 |
} |
|
481 |
||
482 |
return doStringConcat(lookup, name, concatType, true, null); |
|
483 |
} |
|
484 |
||
485 |
/** |
|
486 |
* Facilitates the creation of optimized String concatenation methods, that |
|
487 |
* can be used to efficiently concatenate a known number of arguments of |
|
488 |
* known types, possibly after type adaptation and partial evaluation of |
|
489 |
* arguments. Typically used as a <em>bootstrap method</em> for {@code |
|
490 |
* invokedynamic} call sites, to support the <em>string concatenation</em> |
|
491 |
* feature of the Java Programming Language. |
|
492 |
* |
|
493 |
* <p>When the target of the {@code CallSite} returned from this method is |
|
494 |
* invoked, it returns the result of String concatenation, taking all |
|
495 |
* function arguments and constants passed to the linkage method as inputs for |
|
496 |
* concatenation. The target signature is given by {@code concatType}, and |
|
47985
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
497 |
* does not include constants. |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
498 |
* For a target accepting: |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
499 |
* <ul> |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
500 |
* <li>zero inputs, concatenation results in an empty string;</li> |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
501 |
* <li>one input, concatenation results in the single |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
502 |
* input converted as per JLS 5.1.11 "String Conversion"; otherwise</li> |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
503 |
* <li>two or more inputs, the inputs are concatenated as per |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
504 |
* requirements stated in JLS 15.18.1 "String Concatenation Operator +". |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
505 |
* The inputs are converted as per JLS 5.1.11 "String Conversion", |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
506 |
* and combined from left to right.</li> |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
507 |
* </ul> |
35388 | 508 |
* |
509 |
* <p>The concatenation <em>recipe</em> is a String description for the way to |
|
510 |
* construct a concatenated String from the arguments and constants. The |
|
511 |
* recipe is processed from left to right, and each character represents an |
|
512 |
* input to concatenation. Recipe characters mean: |
|
513 |
* |
|
514 |
* <ul> |
|
515 |
* |
|
516 |
* <li><em>\1 (Unicode point 0001)</em>: an ordinary argument. This |
|
517 |
* input is passed through dynamic argument, and is provided during the |
|
518 |
* concatenation method invocation. This input can be null.</li> |
|
519 |
* |
|
520 |
* <li><em>\2 (Unicode point 0002):</em> a constant. This input passed |
|
521 |
* through static bootstrap argument. This constant can be any value |
|
522 |
* representable in constant pool. If necessary, the factory would call |
|
523 |
* {@code toString} to perform a one-time String conversion.</li> |
|
524 |
* |
|
525 |
* <li><em>Any other char value:</em> a single character constant.</li> |
|
526 |
* </ul> |
|
527 |
* |
|
528 |
* <p>Assume the linkage arguments are as follows: |
|
529 |
* |
|
530 |
* <ul> |
|
531 |
* <li>{@code concatType}, describing the {@code CallSite} signature</li> |
|
532 |
* <li>{@code recipe}, describing the String recipe</li> |
|
533 |
* <li>{@code constants}, the vararg array of constants</li> |
|
534 |
* </ul> |
|
535 |
* |
|
536 |
* <p>Then the following linkage invariants must hold: |
|
537 |
* |
|
538 |
* <ul> |
|
47461
60a5d9c77c4c
8187089: StringConcatFactory.makeConcat & makeConcatWithConstants should throw StringConcatException if parameter slot count is over 200
mchung
parents:
47216
diff
changeset
|
539 |
* <li>The number of parameter slots in {@code concatType} is less than |
60a5d9c77c4c
8187089: StringConcatFactory.makeConcat & makeConcatWithConstants should throw StringConcatException if parameter slot count is over 200
mchung
parents:
47216
diff
changeset
|
540 |
* or equal to 200</li> |
35388 | 541 |
* |
542 |
* <li>The parameter count in {@code concatType} equals to number of \1 tags |
|
543 |
* in {@code recipe}</li> |
|
544 |
* |
|
545 |
* <li>The return type in {@code concatType} is assignable |
|
546 |
* from {@link java.lang.String}, and matches the return type of the |
|
547 |
* returned {@link MethodHandle}</li> |
|
548 |
* |
|
549 |
* <li>The number of elements in {@code constants} equals to number of \2 |
|
550 |
* tags in {@code recipe}</li> |
|
551 |
* </ul> |
|
552 |
* |
|
553 |
* @param lookup Represents a lookup context with the accessibility |
|
47985
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
554 |
* privileges of the caller. Specifically, the lookup |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
555 |
* context must have |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
556 |
* <a href="MethodHandles.Lookup.html#privacc">private access</a> |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
557 |
* privileges. |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
558 |
* When used with {@code invokedynamic}, this is stacked |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
559 |
* automatically by the VM. |
35388 | 560 |
* @param name The name of the method to implement. This name is |
561 |
* arbitrary, and has no meaning for this linkage method. |
|
562 |
* When used with {@code invokedynamic}, this is provided |
|
563 |
* by the {@code NameAndType} of the {@code InvokeDynamic} |
|
564 |
* structure and is stacked automatically by the VM. |
|
565 |
* @param concatType The expected signature of the {@code CallSite}. The |
|
566 |
* parameter types represent the types of dynamic concatenation |
|
567 |
* arguments; the return type is always assignable from {@link |
|
568 |
* java.lang.String}. When used with {@code |
|
569 |
* invokedynamic}, this is provided by the {@code |
|
570 |
* NameAndType} of the {@code InvokeDynamic} structure and |
|
571 |
* is stacked automatically by the VM. |
|
572 |
* @param recipe Concatenation recipe, described above. |
|
573 |
* @param constants A vararg parameter representing the constants passed to |
|
574 |
* the linkage method. |
|
575 |
* @return a CallSite whose target can be used to perform String |
|
576 |
* concatenation, with dynamic concatenation arguments described by the given |
|
577 |
* {@code concatType}. |
|
578 |
* @throws StringConcatException If any of the linkage invariants described |
|
47985
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
579 |
* here are violated, or the lookup context |
80a058c0f993
8187237: Need to define the behaviour for 0 and 1 argument method type in StringConcatFactory.makeConcat
psandoz
parents:
47478
diff
changeset
|
580 |
* does not have private access privileges. |
35388 | 581 |
* @throws NullPointerException If any of the incoming arguments is null, or |
582 |
* any constant in {@code recipe} is null. |
|
583 |
* This will never happen when a bootstrap method |
|
584 |
* is called with invokedynamic. |
|
585 |
* @apiNote Code generators have three distinct ways to process a constant |
|
586 |
* string operand S in a string concatenation expression. First, S can be |
|
587 |
* materialized as a reference (using ldc) and passed as an ordinary argument |
|
588 |
* (recipe '\1'). Or, S can be stored in the constant pool and passed as a |
|
589 |
* constant (recipe '\2') . Finally, if S contains neither of the recipe |
|
590 |
* tag characters ('\1', '\2') then S can be interpolated into the recipe |
|
591 |
* itself, causing its characters to be inserted into the result. |
|
592 |
* |
|
593 |
* @jls 5.1.11 String Conversion |
|
594 |
* @jls 15.18.1 String Concatenation Operator + |
|
595 |
*/ |
|
596 |
public static CallSite makeConcatWithConstants(MethodHandles.Lookup lookup, |
|
597 |
String name, |
|
598 |
MethodType concatType, |
|
599 |
String recipe, |
|
600 |
Object... constants) throws StringConcatException { |
|
601 |
if (DEBUG) { |
|
602 |
System.out.println("StringConcatFactory " + STRATEGY + " is here for " + concatType + ", {" + recipe + "}, " + Arrays.toString(constants)); |
|
603 |
} |
|
604 |
||
605 |
return doStringConcat(lookup, name, concatType, false, recipe, constants); |
|
606 |
} |
|
607 |
||
608 |
private static CallSite doStringConcat(MethodHandles.Lookup lookup, |
|
609 |
String name, |
|
610 |
MethodType concatType, |
|
611 |
boolean generateRecipe, |
|
612 |
String recipe, |
|
613 |
Object... constants) throws StringConcatException { |
|
614 |
Objects.requireNonNull(lookup, "Lookup is null"); |
|
615 |
Objects.requireNonNull(name, "Name is null"); |
|
616 |
Objects.requireNonNull(concatType, "Concat type is null"); |
|
617 |
Objects.requireNonNull(constants, "Constants are null"); |
|
618 |
||
619 |
for (Object o : constants) { |
|
620 |
Objects.requireNonNull(o, "Cannot accept null constants"); |
|
621 |
} |
|
622 |
||
35776
dd5df83fcc0d
8149459: StringConcatFactory should be synced up with LambdaMetafactory
shade
parents:
35700
diff
changeset
|
623 |
if ((lookup.lookupModes() & MethodHandles.Lookup.PRIVATE) == 0) { |
40810
b88d5910ea1e
8165492: Reduce number of lambda forms generated by MethodHandleInlineCopyStrategy
redestad
parents:
40272
diff
changeset
|
624 |
throw new StringConcatException("Invalid caller: " + |
b88d5910ea1e
8165492: Reduce number of lambda forms generated by MethodHandleInlineCopyStrategy
redestad
parents:
40272
diff
changeset
|
625 |
lookup.lookupClass().getName()); |
35776
dd5df83fcc0d
8149459: StringConcatFactory should be synced up with LambdaMetafactory
shade
parents:
35700
diff
changeset
|
626 |
} |
dd5df83fcc0d
8149459: StringConcatFactory should be synced up with LambdaMetafactory
shade
parents:
35700
diff
changeset
|
627 |
|
35388 | 628 |
int cCount = 0; |
629 |
int oCount = 0; |
|
630 |
if (generateRecipe) { |
|
631 |
// Mock the recipe to reuse the concat generator code |
|
632 |
char[] value = new char[concatType.parameterCount()]; |
|
633 |
Arrays.fill(value, TAG_ARG); |
|
634 |
recipe = new String(value); |
|
635 |
oCount = concatType.parameterCount(); |
|
636 |
} else { |
|
637 |
Objects.requireNonNull(recipe, "Recipe is null"); |
|
638 |
||
639 |
for (int i = 0; i < recipe.length(); i++) { |
|
640 |
char c = recipe.charAt(i); |
|
641 |
if (c == TAG_CONST) cCount++; |
|
642 |
if (c == TAG_ARG) oCount++; |
|
643 |
} |
|
644 |
} |
|
645 |
||
646 |
if (oCount != concatType.parameterCount()) { |
|
647 |
throw new StringConcatException( |
|
648 |
"Mismatched number of concat arguments: recipe wants " + |
|
649 |
oCount + |
|
650 |
" arguments, but signature provides " + |
|
651 |
concatType.parameterCount()); |
|
652 |
} |
|
653 |
||
654 |
if (cCount != constants.length) { |
|
655 |
throw new StringConcatException( |
|
656 |
"Mismatched number of concat constants: recipe wants " + |
|
657 |
cCount + |
|
658 |
" constants, but only " + |
|
659 |
constants.length + |
|
660 |
" are passed"); |
|
661 |
} |
|
662 |
||
663 |
if (!concatType.returnType().isAssignableFrom(String.class)) { |
|
664 |
throw new StringConcatException( |
|
665 |
"The return type should be compatible with String, but it is " + |
|
666 |
concatType.returnType()); |
|
667 |
} |
|
668 |
||
47461
60a5d9c77c4c
8187089: StringConcatFactory.makeConcat & makeConcatWithConstants should throw StringConcatException if parameter slot count is over 200
mchung
parents:
47216
diff
changeset
|
669 |
if (concatType.parameterSlotCount() > MAX_INDY_CONCAT_ARG_SLOTS) { |
35388 | 670 |
throw new StringConcatException("Too many concat argument slots: " + |
47461
60a5d9c77c4c
8187089: StringConcatFactory.makeConcat & makeConcatWithConstants should throw StringConcatException if parameter slot count is over 200
mchung
parents:
47216
diff
changeset
|
671 |
concatType.parameterSlotCount() + |
35388 | 672 |
", can only accept " + |
673 |
MAX_INDY_CONCAT_ARG_SLOTS); |
|
674 |
} |
|
675 |
||
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
676 |
String className = getClassName(lookup.lookupClass()); |
35388 | 677 |
MethodType mt = adaptType(concatType); |
678 |
Recipe rec = new Recipe(recipe, constants); |
|
679 |
||
680 |
MethodHandle mh; |
|
681 |
if (CACHE_ENABLE) { |
|
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
682 |
Key key = new Key(className, mt, rec); |
35388 | 683 |
mh = CACHE.get(key); |
684 |
if (mh == null) { |
|
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
685 |
mh = generate(lookup, className, mt, rec); |
35388 | 686 |
CACHE.put(key, mh); |
687 |
} |
|
688 |
} else { |
|
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
689 |
mh = generate(lookup, className, mt, rec); |
35388 | 690 |
} |
691 |
return new ConstantCallSite(mh.asType(concatType)); |
|
692 |
} |
|
693 |
||
694 |
/** |
|
695 |
* Adapt method type to an API we are going to use. |
|
696 |
* |
|
697 |
* This strips the concrete classes from the signatures, thus preventing |
|
698 |
* class leakage when we cache the concatenation stubs. |
|
699 |
* |
|
700 |
* @param args actual argument types |
|
701 |
* @return argument types the strategy is going to use |
|
702 |
*/ |
|
703 |
private static MethodType adaptType(MethodType args) { |
|
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
704 |
Class<?>[] ptypes = null; |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
705 |
for (int i = 0; i < args.parameterCount(); i++) { |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
706 |
Class<?> ptype = args.parameterType(i); |
35388 | 707 |
if (!ptype.isPrimitive() && |
708 |
ptype != String.class && |
|
709 |
ptype != Object.class) { // truncate to Object |
|
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
710 |
if (ptypes == null) { |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
711 |
ptypes = args.parameterArray(); |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
712 |
} |
35388 | 713 |
ptypes[i] = Object.class; |
714 |
} |
|
715 |
// else other primitives or String or Object (unchanged) |
|
716 |
} |
|
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
717 |
return (ptypes != null) |
35388 | 718 |
? MethodType.methodType(args.returnType(), ptypes) |
719 |
: args; |
|
720 |
} |
|
721 |
||
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
722 |
private static String getClassName(Class<?> hostClass) throws StringConcatException { |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
723 |
/* |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
724 |
When cache is enabled, we want to cache as much as we can. |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
725 |
|
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
726 |
However, there are two peculiarities: |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
727 |
|
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
728 |
a) The generated class should stay within the same package as the |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
729 |
host class, to allow Unsafe.defineAnonymousClass access controls |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
730 |
to work properly. JDK may choose to fail with IllegalAccessException |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
731 |
when accessing a VM anonymous class with non-privileged callers, |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
732 |
see JDK-8058575. |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
733 |
|
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
734 |
b) If we mark the stub with some prefix, say, derived from the package |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
735 |
name because of (a), we can technically use that stub in other packages. |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
736 |
But the call stack traces would be extremely puzzling to unsuspecting users |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
737 |
and profiling tools: whatever stub wins the race, would be linked in all |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
738 |
similar callsites. |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
739 |
|
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
740 |
Therefore, we set the class prefix to match the host class package, and use |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
741 |
the prefix as the cache key too. This only affects BC_* strategies, and only when |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
742 |
cache is enabled. |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
743 |
*/ |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
744 |
|
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
745 |
switch (STRATEGY) { |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
746 |
case BC_SB: |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
747 |
case BC_SB_SIZED: |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
748 |
case BC_SB_SIZED_EXACT: { |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
749 |
if (CACHE_ENABLE) { |
36511 | 750 |
String pkgName = hostClass.getPackageName(); |
751 |
return (pkgName != null && !pkgName.isEmpty() ? pkgName.replace('.', '/') + "/" : "") + "Stubs$$StringConcat"; |
|
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
752 |
} else { |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
753 |
return hostClass.getName().replace('.', '/') + "$$StringConcat"; |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
754 |
} |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
755 |
} |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
756 |
case MH_SB_SIZED: |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
757 |
case MH_SB_SIZED_EXACT: |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
758 |
case MH_INLINE_SIZED_EXACT: |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
759 |
// MethodHandle strategies do not need a class name. |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
760 |
return ""; |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
761 |
default: |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
762 |
throw new StringConcatException("Concatenation strategy " + STRATEGY + " is not implemented"); |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
763 |
} |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
764 |
} |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
765 |
|
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
766 |
private static MethodHandle generate(Lookup lookup, String className, MethodType mt, Recipe recipe) throws StringConcatException { |
35388 | 767 |
try { |
768 |
switch (STRATEGY) { |
|
769 |
case BC_SB: |
|
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
770 |
return BytecodeStringBuilderStrategy.generate(lookup, className, mt, recipe, Mode.DEFAULT); |
35388 | 771 |
case BC_SB_SIZED: |
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
772 |
return BytecodeStringBuilderStrategy.generate(lookup, className, mt, recipe, Mode.SIZED); |
35388 | 773 |
case BC_SB_SIZED_EXACT: |
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
774 |
return BytecodeStringBuilderStrategy.generate(lookup, className, mt, recipe, Mode.SIZED_EXACT); |
35388 | 775 |
case MH_SB_SIZED: |
776 |
return MethodHandleStringBuilderStrategy.generate(mt, recipe, Mode.SIZED); |
|
777 |
case MH_SB_SIZED_EXACT: |
|
778 |
return MethodHandleStringBuilderStrategy.generate(mt, recipe, Mode.SIZED_EXACT); |
|
779 |
case MH_INLINE_SIZED_EXACT: |
|
780 |
return MethodHandleInlineCopyStrategy.generate(mt, recipe); |
|
781 |
default: |
|
782 |
throw new StringConcatException("Concatenation strategy " + STRATEGY + " is not implemented"); |
|
783 |
} |
|
42105
d04ede5f10e7
8163553: java.lang.LinkageError from test java/lang/ThreadGroup/Stop.java
psandoz
parents:
40810
diff
changeset
|
784 |
} catch (Error | StringConcatException e) { |
d04ede5f10e7
8163553: java.lang.LinkageError from test java/lang/ThreadGroup/Stop.java
psandoz
parents:
40810
diff
changeset
|
785 |
// Pass through any error or existing StringConcatException |
d04ede5f10e7
8163553: java.lang.LinkageError from test java/lang/ThreadGroup/Stop.java
psandoz
parents:
40810
diff
changeset
|
786 |
throw e; |
35388 | 787 |
} catch (Throwable t) { |
788 |
throw new StringConcatException("Generator failed", t); |
|
789 |
} |
|
790 |
} |
|
791 |
||
792 |
private enum Mode { |
|
793 |
DEFAULT(false, false), |
|
794 |
SIZED(true, false), |
|
795 |
SIZED_EXACT(true, true); |
|
796 |
||
797 |
private final boolean sized; |
|
798 |
private final boolean exact; |
|
799 |
||
800 |
Mode(boolean sized, boolean exact) { |
|
801 |
this.sized = sized; |
|
802 |
this.exact = exact; |
|
803 |
} |
|
804 |
||
805 |
boolean isSized() { |
|
806 |
return sized; |
|
807 |
} |
|
808 |
||
809 |
boolean isExact() { |
|
810 |
return exact; |
|
811 |
} |
|
812 |
} |
|
813 |
||
814 |
/** |
|
815 |
* Bytecode StringBuilder strategy. |
|
816 |
* |
|
817 |
* <p>This strategy operates in three modes, gated by {@link Mode}. |
|
818 |
* |
|
819 |
* <p><b>{@link Strategy#BC_SB}: "bytecode StringBuilder".</b> |
|
820 |
* |
|
821 |
* <p>This strategy spins up the bytecode that has the same StringBuilder |
|
822 |
* chain javac would otherwise emit. This strategy uses only the public API, |
|
823 |
* and comes as the baseline for the current JDK behavior. On other words, |
|
824 |
* this strategy moves the javac generated bytecode to runtime. The |
|
825 |
* generated bytecode is loaded via Unsafe.defineAnonymousClass, but with |
|
826 |
* the caller class coming from the BSM -- in other words, the protection |
|
827 |
* guarantees are inherited from the method where invokedynamic was |
|
828 |
* originally called. This means, among other things, that the bytecode is |
|
829 |
* verified for all non-JDK uses. |
|
830 |
* |
|
831 |
* <p><b>{@link Strategy#BC_SB_SIZED}: "bytecode StringBuilder, but |
|
832 |
* sized".</b> |
|
833 |
* |
|
834 |
* <p>This strategy acts similarly to {@link Strategy#BC_SB}, but it also |
|
835 |
* tries to guess the capacity required for StringBuilder to accept all |
|
836 |
* arguments without resizing. This strategy only makes an educated guess: |
|
837 |
* it only guesses the space required for known types (e.g. primitives and |
|
838 |
* Strings), but does not otherwise convert arguments. Therefore, the |
|
839 |
* capacity estimate may be wrong, and StringBuilder may have to |
|
840 |
* transparently resize or trim when doing the actual concatenation. While |
|
841 |
* this does not constitute a correctness issue (in the end, that what BC_SB |
|
842 |
* has to do anyway), this does pose a potential performance problem. |
|
843 |
* |
|
844 |
* <p><b>{@link Strategy#BC_SB_SIZED_EXACT}: "bytecode StringBuilder, but |
|
845 |
* sized exactly".</b> |
|
846 |
* |
|
847 |
* <p>This strategy improves on @link Strategy#BC_SB_SIZED}, by first |
|
848 |
* converting all arguments to String in order to get the exact capacity |
|
849 |
* StringBuilder should have. The conversion is done via the public |
|
850 |
* String.valueOf and/or Object.toString methods, and does not touch any |
|
851 |
* private String API. |
|
852 |
*/ |
|
853 |
private static final class BytecodeStringBuilderStrategy { |
|
854 |
static final Unsafe UNSAFE = Unsafe.getUnsafe(); |
|
855 |
static final int CLASSFILE_VERSION = 52; |
|
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
856 |
static final String METHOD_NAME = "concat"; |
35388 | 857 |
|
858 |
private BytecodeStringBuilderStrategy() { |
|
859 |
// no instantiation |
|
860 |
} |
|
861 |
||
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
862 |
private static MethodHandle generate(Lookup lookup, String className, MethodType args, Recipe recipe, Mode mode) throws Exception { |
35388 | 863 |
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES); |
864 |
||
865 |
cw.visit(CLASSFILE_VERSION, |
|
866 |
ACC_SUPER + ACC_PUBLIC + ACC_FINAL + ACC_SYNTHETIC, |
|
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
867 |
className, // Unsafe.defineAnonymousClass would append an unique ID |
35388 | 868 |
null, |
869 |
"java/lang/Object", |
|
870 |
null |
|
871 |
); |
|
872 |
||
873 |
MethodVisitor mv = cw.visitMethod( |
|
874 |
ACC_PUBLIC + ACC_STATIC + ACC_FINAL, |
|
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
875 |
METHOD_NAME, |
35388 | 876 |
args.toMethodDescriptorString(), |
877 |
null, |
|
878 |
null); |
|
879 |
||
880 |
mv.visitAnnotation("Ljdk/internal/vm/annotation/ForceInline;", true); |
|
881 |
mv.visitCode(); |
|
882 |
||
883 |
Class<?>[] arr = args.parameterArray(); |
|
884 |
boolean[] guaranteedNonNull = new boolean[arr.length]; |
|
885 |
||
886 |
if (mode.isExact()) { |
|
887 |
/* |
|
888 |
In exact mode, we need to convert all arguments to their String representations, |
|
889 |
as this allows to compute their String sizes exactly. We cannot use private |
|
890 |
methods for primitives in here, therefore we need to convert those as well. |
|
891 |
||
892 |
We also record what arguments are guaranteed to be non-null as the result |
|
893 |
of the conversion. String.valueOf does the null checks for us. The only |
|
894 |
corner case to take care of is String.valueOf(Object) returning null itself. |
|
895 |
||
896 |
Also, if any conversion happened, then the slot indices in the incoming |
|
897 |
arguments are not equal to the final local maps. The only case this may break |
|
898 |
is when converting 2-slot long/double argument to 1-slot String. Therefore, |
|
899 |
we get away with tracking modified offset, since no conversion can overwrite |
|
900 |
the upcoming the argument. |
|
901 |
*/ |
|
902 |
||
903 |
int off = 0; |
|
904 |
int modOff = 0; |
|
905 |
for (int c = 0; c < arr.length; c++) { |
|
906 |
Class<?> cl = arr[c]; |
|
907 |
if (cl == String.class) { |
|
908 |
if (off != modOff) { |
|
909 |
mv.visitIntInsn(getLoadOpcode(cl), off); |
|
910 |
mv.visitIntInsn(ASTORE, modOff); |
|
911 |
} |
|
912 |
} else { |
|
913 |
mv.visitIntInsn(getLoadOpcode(cl), off); |
|
914 |
mv.visitMethodInsn( |
|
915 |
INVOKESTATIC, |
|
916 |
"java/lang/String", |
|
917 |
"valueOf", |
|
918 |
getStringValueOfDesc(cl), |
|
919 |
false |
|
920 |
); |
|
921 |
mv.visitIntInsn(ASTORE, modOff); |
|
922 |
arr[c] = String.class; |
|
923 |
guaranteedNonNull[c] = cl.isPrimitive(); |
|
924 |
} |
|
925 |
off += getParameterSize(cl); |
|
926 |
modOff += getParameterSize(String.class); |
|
927 |
} |
|
928 |
} |
|
929 |
||
930 |
if (mode.isSized()) { |
|
931 |
/* |
|
932 |
When operating in sized mode (this includes exact mode), it makes sense to make |
|
933 |
StringBuilder append chains look familiar to OptimizeStringConcat. For that, we |
|
934 |
need to do null-checks early, not make the append chain shape simpler. |
|
935 |
*/ |
|
936 |
||
937 |
int off = 0; |
|
938 |
for (RecipeElement el : recipe.getElements()) { |
|
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
939 |
switch (el.getTag()) { |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
940 |
case TAG_CONST: |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
941 |
// Guaranteed non-null, no null check required. |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
942 |
break; |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
943 |
case TAG_ARG: |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
944 |
// Null-checks are needed only for String arguments, and when a previous stage |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
945 |
// did not do implicit null-checks. If a String is null, we eagerly replace it |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
946 |
// with "null" constant. Note, we omit Objects here, because we don't call |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
947 |
// .length() on them down below. |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
948 |
int ac = el.getArgPos(); |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
949 |
Class<?> cl = arr[ac]; |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
950 |
if (cl == String.class && !guaranteedNonNull[ac]) { |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
951 |
Label l0 = new Label(); |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
952 |
mv.visitIntInsn(ALOAD, off); |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
953 |
mv.visitJumpInsn(IFNONNULL, l0); |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
954 |
mv.visitLdcInsn("null"); |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
955 |
mv.visitIntInsn(ASTORE, off); |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
956 |
mv.visitLabel(l0); |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
957 |
} |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
958 |
off += getParameterSize(cl); |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
959 |
break; |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
960 |
default: |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
961 |
throw new StringConcatException("Unhandled tag: " + el.getTag()); |
35388 | 962 |
} |
963 |
} |
|
964 |
} |
|
965 |
||
966 |
// Prepare StringBuilder instance |
|
967 |
mv.visitTypeInsn(NEW, "java/lang/StringBuilder"); |
|
968 |
mv.visitInsn(DUP); |
|
969 |
||
970 |
if (mode.isSized()) { |
|
971 |
/* |
|
972 |
Sized mode requires us to walk through the arguments, and estimate the final length. |
|
973 |
In exact mode, this will operate on Strings only. This code would accumulate the |
|
974 |
final length on stack. |
|
975 |
*/ |
|
976 |
int len = 0; |
|
977 |
int off = 0; |
|
978 |
||
979 |
mv.visitInsn(ICONST_0); |
|
980 |
||
981 |
for (RecipeElement el : recipe.getElements()) { |
|
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
982 |
switch (el.getTag()) { |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
983 |
case TAG_CONST: |
46902
c6cefe631b18
8186500: StringConcatFactory.makeConcatWithConstants throws AssertionError when recipe contains non-String constants
redestad
parents:
42105
diff
changeset
|
984 |
len += el.getValue().length(); |
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
985 |
break; |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
986 |
case TAG_ARG: |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
987 |
/* |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
988 |
If an argument is String, then we can call .length() on it. Sized/Exact modes have |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
989 |
converted arguments for us. If an argument is primitive, we can provide a guess |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
990 |
for its String representation size. |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
991 |
*/ |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
992 |
Class<?> cl = arr[el.getArgPos()]; |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
993 |
if (cl == String.class) { |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
994 |
mv.visitIntInsn(ALOAD, off); |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
995 |
mv.visitMethodInsn( |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
996 |
INVOKEVIRTUAL, |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
997 |
"java/lang/String", |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
998 |
"length", |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
999 |
"()I", |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1000 |
false |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1001 |
); |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1002 |
mv.visitInsn(IADD); |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1003 |
} else if (cl.isPrimitive()) { |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1004 |
len += estimateSize(cl); |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1005 |
} |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1006 |
off += getParameterSize(cl); |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1007 |
break; |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1008 |
default: |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1009 |
throw new StringConcatException("Unhandled tag: " + el.getTag()); |
35388 | 1010 |
} |
1011 |
} |
|
1012 |
||
1013 |
// Constants have non-zero length, mix in |
|
1014 |
if (len > 0) { |
|
1015 |
iconst(mv, len); |
|
1016 |
mv.visitInsn(IADD); |
|
1017 |
} |
|
1018 |
||
1019 |
mv.visitMethodInsn( |
|
1020 |
INVOKESPECIAL, |
|
1021 |
"java/lang/StringBuilder", |
|
1022 |
"<init>", |
|
1023 |
"(I)V", |
|
1024 |
false |
|
1025 |
); |
|
1026 |
} else { |
|
1027 |
mv.visitMethodInsn( |
|
1028 |
INVOKESPECIAL, |
|
1029 |
"java/lang/StringBuilder", |
|
1030 |
"<init>", |
|
1031 |
"()V", |
|
1032 |
false |
|
1033 |
); |
|
1034 |
} |
|
1035 |
||
1036 |
// At this point, we have a blank StringBuilder on stack, fill it in with .append calls. |
|
1037 |
{ |
|
1038 |
int off = 0; |
|
1039 |
for (RecipeElement el : recipe.getElements()) { |
|
1040 |
String desc; |
|
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1041 |
switch (el.getTag()) { |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1042 |
case TAG_CONST: |
46902
c6cefe631b18
8186500: StringConcatFactory.makeConcatWithConstants throws AssertionError when recipe contains non-String constants
redestad
parents:
42105
diff
changeset
|
1043 |
mv.visitLdcInsn(el.getValue()); |
c6cefe631b18
8186500: StringConcatFactory.makeConcatWithConstants throws AssertionError when recipe contains non-String constants
redestad
parents:
42105
diff
changeset
|
1044 |
desc = getSBAppendDesc(String.class); |
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1045 |
break; |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1046 |
case TAG_ARG: |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1047 |
Class<?> cl = arr[el.getArgPos()]; |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1048 |
mv.visitVarInsn(getLoadOpcode(cl), off); |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1049 |
off += getParameterSize(cl); |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1050 |
desc = getSBAppendDesc(cl); |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1051 |
break; |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1052 |
default: |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1053 |
throw new StringConcatException("Unhandled tag: " + el.getTag()); |
35388 | 1054 |
} |
40256
c5e03eaf7ba2
8163370: Reduce number of classes loaded by common usage of java.lang.invoke
redestad
parents:
38868
diff
changeset
|
1055 |
|
35388 | 1056 |
mv.visitMethodInsn( |
1057 |
INVOKEVIRTUAL, |
|
1058 |
"java/lang/StringBuilder", |
|
1059 |
"append", |
|
1060 |
desc, |
|
1061 |
false |
|
1062 |
); |
|
1063 |
} |
|
1064 |
} |
|
1065 |
||
1066 |
if (DEBUG && mode.isExact()) { |
|
1067 |
/* |
|
1068 |
Exactness checks compare the final StringBuilder.capacity() with a resulting |
|
1069 |
String.length(). If these values disagree, that means StringBuilder had to perform |
|
1070 |
storage trimming, which defeats the purpose of exact strategies. |
|
1071 |
*/ |
|
1072 |
||
35639
f34e7e8b4eac
8148787: StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
shade
parents:
35401
diff
changeset
|
1073 |
/* |
f34e7e8b4eac
8148787: StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
shade
parents:
35401
diff
changeset
|
1074 |
The logic for this check is as follows: |
f34e7e8b4eac
8148787: StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
shade
parents:
35401
diff
changeset
|
1075 |
|
f34e7e8b4eac
8148787: StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
shade
parents:
35401
diff
changeset
|
1076 |
Stack before: Op: |
f34e7e8b4eac
8148787: StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
shade
parents:
35401
diff
changeset
|
1077 |
(SB) dup, dup |
f34e7e8b4eac
8148787: StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
shade
parents:
35401
diff
changeset
|
1078 |
(SB, SB, SB) capacity() |
f34e7e8b4eac
8148787: StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
shade
parents:
35401
diff
changeset
|
1079 |
(int, SB, SB) swap |
f34e7e8b4eac
8148787: StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
shade
parents:
35401
diff
changeset
|
1080 |
(SB, int, SB) toString() |
f34e7e8b4eac
8148787: StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
shade
parents:
35401
diff
changeset
|
1081 |
(S, int, SB) length() |
f34e7e8b4eac
8148787: StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
shade
parents:
35401
diff
changeset
|
1082 |
(int, int, SB) if_icmpeq |
f34e7e8b4eac
8148787: StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
shade
parents:
35401
diff
changeset
|
1083 |
(SB) <end> |
f34e7e8b4eac
8148787: StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
shade
parents:
35401
diff
changeset
|
1084 |
|
f34e7e8b4eac
8148787: StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
shade
parents:
35401
diff
changeset
|
1085 |
Note that it leaves the same StringBuilder on exit, like the one on enter. |
f34e7e8b4eac
8148787: StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
shade
parents:
35401
diff
changeset
|
1086 |
*/ |
f34e7e8b4eac
8148787: StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
shade
parents:
35401
diff
changeset
|
1087 |
|
f34e7e8b4eac
8148787: StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
shade
parents:
35401
diff
changeset
|
1088 |
mv.visitInsn(DUP); |
35388 | 1089 |
mv.visitInsn(DUP); |
1090 |
||
1091 |
mv.visitMethodInsn( |
|
1092 |
INVOKEVIRTUAL, |
|
1093 |
"java/lang/StringBuilder", |
|
1094 |
"capacity", |
|
1095 |
"()I", |
|
1096 |
false |
|
1097 |
); |
|
1098 |
||
35639
f34e7e8b4eac
8148787: StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
shade
parents:
35401
diff
changeset
|
1099 |
mv.visitInsn(SWAP); |
35388 | 1100 |
|
1101 |
mv.visitMethodInsn( |
|
1102 |
INVOKEVIRTUAL, |
|
1103 |
"java/lang/StringBuilder", |
|
1104 |
"toString", |
|
1105 |
"()Ljava/lang/String;", |
|
1106 |
false |
|
1107 |
); |
|
1108 |
||
1109 |
mv.visitMethodInsn( |
|
1110 |
INVOKEVIRTUAL, |
|
1111 |
"java/lang/String", |
|
1112 |
"length", |
|
1113 |
"()I", |
|
1114 |
false |
|
1115 |
); |
|
1116 |
||
1117 |
Label l0 = new Label(); |
|
1118 |
mv.visitJumpInsn(IF_ICMPEQ, l0); |
|
1119 |
||
1120 |
mv.visitTypeInsn(NEW, "java/lang/AssertionError"); |
|
1121 |
mv.visitInsn(DUP); |
|
1122 |
mv.visitLdcInsn("Failed exactness check"); |
|
1123 |
mv.visitMethodInsn(INVOKESPECIAL, |
|
1124 |
"java/lang/AssertionError", |
|
1125 |
"<init>", |
|
1126 |
"(Ljava/lang/Object;)V", |
|
1127 |
false); |
|
1128 |
mv.visitInsn(ATHROW); |
|
1129 |
||
1130 |
mv.visitLabel(l0); |
|
1131 |
} |
|
1132 |
||
35639
f34e7e8b4eac
8148787: StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
shade
parents:
35401
diff
changeset
|
1133 |
mv.visitMethodInsn( |
f34e7e8b4eac
8148787: StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
shade
parents:
35401
diff
changeset
|
1134 |
INVOKEVIRTUAL, |
f34e7e8b4eac
8148787: StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
shade
parents:
35401
diff
changeset
|
1135 |
"java/lang/StringBuilder", |
f34e7e8b4eac
8148787: StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
shade
parents:
35401
diff
changeset
|
1136 |
"toString", |
f34e7e8b4eac
8148787: StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
shade
parents:
35401
diff
changeset
|
1137 |
"()Ljava/lang/String;", |
f34e7e8b4eac
8148787: StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
shade
parents:
35401
diff
changeset
|
1138 |
false |
f34e7e8b4eac
8148787: StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
shade
parents:
35401
diff
changeset
|
1139 |
); |
f34e7e8b4eac
8148787: StringConcatFactory exactness check produces bad bytecode when a non-arg concat is requested
shade
parents:
35401
diff
changeset
|
1140 |
|
35388 | 1141 |
mv.visitInsn(ARETURN); |
1142 |
||
1143 |
mv.visitMaxs(-1, -1); |
|
1144 |
mv.visitEnd(); |
|
1145 |
cw.visitEnd(); |
|
1146 |
||
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
1147 |
byte[] classBytes = cw.toByteArray(); |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
1148 |
try { |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
1149 |
Class<?> hostClass = lookup.lookupClass(); |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
1150 |
Class<?> innerClass = UNSAFE.defineAnonymousClass(hostClass, classBytes, null); |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
1151 |
UNSAFE.ensureClassInitialized(innerClass); |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
1152 |
dumpIfEnabled(innerClass.getName(), classBytes); |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
1153 |
return Lookup.IMPL_LOOKUP.findStatic(innerClass, METHOD_NAME, args); |
42105
d04ede5f10e7
8163553: java.lang.LinkageError from test java/lang/ThreadGroup/Stop.java
psandoz
parents:
40810
diff
changeset
|
1154 |
} catch (Exception e) { |
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
1155 |
dumpIfEnabled(className + "$$FAILED", classBytes); |
42105
d04ede5f10e7
8163553: java.lang.LinkageError from test java/lang/ThreadGroup/Stop.java
psandoz
parents:
40810
diff
changeset
|
1156 |
throw new StringConcatException("Exception while spinning the class", e); |
35776
dd5df83fcc0d
8149459: StringConcatFactory should be synced up with LambdaMetafactory
shade
parents:
35700
diff
changeset
|
1157 |
} |
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
1158 |
} |
35776
dd5df83fcc0d
8149459: StringConcatFactory should be synced up with LambdaMetafactory
shade
parents:
35700
diff
changeset
|
1159 |
|
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
1160 |
private static void dumpIfEnabled(String name, byte[] bytes) { |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
1161 |
if (DUMPER != null) { |
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
1162 |
DUMPER.dumpClass(name, bytes); |
35388 | 1163 |
} |
1164 |
} |
|
1165 |
||
1166 |
private static String getSBAppendDesc(Class<?> cl) { |
|
1167 |
if (cl.isPrimitive()) { |
|
1168 |
if (cl == Integer.TYPE || cl == Byte.TYPE || cl == Short.TYPE) { |
|
1169 |
return "(I)Ljava/lang/StringBuilder;"; |
|
1170 |
} else if (cl == Boolean.TYPE) { |
|
1171 |
return "(Z)Ljava/lang/StringBuilder;"; |
|
1172 |
} else if (cl == Character.TYPE) { |
|
1173 |
return "(C)Ljava/lang/StringBuilder;"; |
|
1174 |
} else if (cl == Double.TYPE) { |
|
1175 |
return "(D)Ljava/lang/StringBuilder;"; |
|
1176 |
} else if (cl == Float.TYPE) { |
|
1177 |
return "(F)Ljava/lang/StringBuilder;"; |
|
1178 |
} else if (cl == Long.TYPE) { |
|
1179 |
return "(J)Ljava/lang/StringBuilder;"; |
|
1180 |
} else { |
|
1181 |
throw new IllegalStateException("Unhandled primitive StringBuilder.append: " + cl); |
|
1182 |
} |
|
1183 |
} else if (cl == String.class) { |
|
1184 |
return "(Ljava/lang/String;)Ljava/lang/StringBuilder;"; |
|
1185 |
} else { |
|
1186 |
return "(Ljava/lang/Object;)Ljava/lang/StringBuilder;"; |
|
1187 |
} |
|
1188 |
} |
|
1189 |
||
1190 |
private static String getStringValueOfDesc(Class<?> cl) { |
|
1191 |
if (cl.isPrimitive()) { |
|
1192 |
if (cl == Integer.TYPE || cl == Byte.TYPE || cl == Short.TYPE) { |
|
1193 |
return "(I)Ljava/lang/String;"; |
|
1194 |
} else if (cl == Boolean.TYPE) { |
|
1195 |
return "(Z)Ljava/lang/String;"; |
|
1196 |
} else if (cl == Character.TYPE) { |
|
1197 |
return "(C)Ljava/lang/String;"; |
|
1198 |
} else if (cl == Double.TYPE) { |
|
1199 |
return "(D)Ljava/lang/String;"; |
|
1200 |
} else if (cl == Float.TYPE) { |
|
1201 |
return "(F)Ljava/lang/String;"; |
|
1202 |
} else if (cl == Long.TYPE) { |
|
1203 |
return "(J)Ljava/lang/String;"; |
|
1204 |
} else { |
|
1205 |
throw new IllegalStateException("Unhandled String.valueOf: " + cl); |
|
1206 |
} |
|
1207 |
} else if (cl == String.class) { |
|
1208 |
return "(Ljava/lang/String;)Ljava/lang/String;"; |
|
1209 |
} else { |
|
1210 |
return "(Ljava/lang/Object;)Ljava/lang/String;"; |
|
1211 |
} |
|
1212 |
} |
|
1213 |
||
1214 |
/** |
|
1215 |
* The following method is copied from |
|
1216 |
* org.objectweb.asm.commons.InstructionAdapter. Part of ASM: a very small |
|
1217 |
* and fast Java bytecode manipulation framework. |
|
1218 |
* Copyright (c) 2000-2005 INRIA, France Telecom All rights reserved. |
|
1219 |
*/ |
|
1220 |
private static void iconst(MethodVisitor mv, final int cst) { |
|
1221 |
if (cst >= -1 && cst <= 5) { |
|
1222 |
mv.visitInsn(Opcodes.ICONST_0 + cst); |
|
1223 |
} else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) { |
|
1224 |
mv.visitIntInsn(Opcodes.BIPUSH, cst); |
|
1225 |
} else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) { |
|
1226 |
mv.visitIntInsn(Opcodes.SIPUSH, cst); |
|
1227 |
} else { |
|
1228 |
mv.visitLdcInsn(cst); |
|
1229 |
} |
|
1230 |
} |
|
1231 |
||
1232 |
private static int getLoadOpcode(Class<?> c) { |
|
1233 |
if (c == Void.TYPE) { |
|
1234 |
throw new InternalError("Unexpected void type of load opcode"); |
|
1235 |
} |
|
1236 |
return ILOAD + getOpcodeOffset(c); |
|
1237 |
} |
|
1238 |
||
1239 |
private static int getOpcodeOffset(Class<?> c) { |
|
1240 |
if (c.isPrimitive()) { |
|
1241 |
if (c == Long.TYPE) { |
|
1242 |
return 1; |
|
1243 |
} else if (c == Float.TYPE) { |
|
1244 |
return 2; |
|
1245 |
} else if (c == Double.TYPE) { |
|
1246 |
return 3; |
|
1247 |
} |
|
1248 |
return 0; |
|
1249 |
} else { |
|
1250 |
return 4; |
|
1251 |
} |
|
1252 |
} |
|
1253 |
||
1254 |
private static int getParameterSize(Class<?> c) { |
|
1255 |
if (c == Void.TYPE) { |
|
1256 |
return 0; |
|
1257 |
} else if (c == Long.TYPE || c == Double.TYPE) { |
|
1258 |
return 2; |
|
1259 |
} |
|
1260 |
return 1; |
|
1261 |
} |
|
1262 |
} |
|
1263 |
||
1264 |
/** |
|
1265 |
* MethodHandle StringBuilder strategy. |
|
1266 |
* |
|
1267 |
* <p>This strategy operates in two modes, gated by {@link Mode}. |
|
1268 |
* |
|
1269 |
* <p><b>{@link Strategy#MH_SB_SIZED}: "MethodHandles StringBuilder, |
|
1270 |
* sized".</b> |
|
1271 |
* |
|
1272 |
* <p>This strategy avoids spinning up the bytecode by building the |
|
1273 |
* computation on MethodHandle combinators. The computation is built with |
|
1274 |
* public MethodHandle APIs, resolved from a public Lookup sequence, and |
|
1275 |
* ends up calling the public StringBuilder API. Therefore, this strategy |
|
1276 |
* does not use any private API at all, even the Unsafe.defineAnonymousClass, |
|
1277 |
* since everything is handled under cover by java.lang.invoke APIs. |
|
1278 |
* |
|
1279 |
* <p><b>{@link Strategy#MH_SB_SIZED_EXACT}: "MethodHandles StringBuilder, |
|
1280 |
* sized exactly".</b> |
|
1281 |
* |
|
1282 |
* <p>This strategy improves on @link Strategy#MH_SB_SIZED}, by first |
|
1283 |
* converting all arguments to String in order to get the exact capacity |
|
1284 |
* StringBuilder should have. The conversion is done via the public |
|
1285 |
* String.valueOf and/or Object.toString methods, and does not touch any |
|
1286 |
* private String API. |
|
1287 |
*/ |
|
1288 |
private static final class MethodHandleStringBuilderStrategy { |
|
1289 |
||
1290 |
private MethodHandleStringBuilderStrategy() { |
|
1291 |
// no instantiation |
|
1292 |
} |
|
1293 |
||
1294 |
private static MethodHandle generate(MethodType mt, Recipe recipe, Mode mode) throws Exception { |
|
1295 |
int pc = mt.parameterCount(); |
|
1296 |
||
1297 |
Class<?>[] ptypes = mt.parameterArray(); |
|
1298 |
MethodHandle[] filters = new MethodHandle[ptypes.length]; |
|
1299 |
for (int i = 0; i < ptypes.length; i++) { |
|
1300 |
MethodHandle filter; |
|
1301 |
switch (mode) { |
|
1302 |
case SIZED: |
|
1303 |
// In sized mode, we convert all references and floats/doubles |
|
1304 |
// to String: there is no specialization for different |
|
1305 |
// classes in StringBuilder API, and it will convert to |
|
1306 |
// String internally anyhow. |
|
1307 |
filter = Stringifiers.forMost(ptypes[i]); |
|
1308 |
break; |
|
1309 |
case SIZED_EXACT: |
|
1310 |
// In exact mode, we convert everything to String: |
|
1311 |
// this helps to compute the storage exactly. |
|
1312 |
filter = Stringifiers.forAny(ptypes[i]); |
|
1313 |
break; |
|
1314 |
default: |
|
1315 |
throw new StringConcatException("Not supported"); |
|
1316 |
} |
|
1317 |
if (filter != null) { |
|
1318 |
filters[i] = filter; |
|
1319 |
ptypes[i] = filter.type().returnType(); |
|
1320 |
} |
|
1321 |
} |
|
1322 |
||
1323 |
MethodHandle[] lengthers = new MethodHandle[pc]; |
|
1324 |
||
1325 |
// Figure out lengths: constants' lengths can be deduced on the spot. |
|
1326 |
// All reference arguments were filtered to String in the combinators below, so we can |
|
1327 |
// call the usual String.length(). Primitive values string sizes can be estimated. |
|
1328 |
int initial = 0; |
|
1329 |
for (RecipeElement el : recipe.getElements()) { |
|
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1330 |
switch (el.getTag()) { |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1331 |
case TAG_CONST: |
46902
c6cefe631b18
8186500: StringConcatFactory.makeConcatWithConstants throws AssertionError when recipe contains non-String constants
redestad
parents:
42105
diff
changeset
|
1332 |
initial += el.getValue().length(); |
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1333 |
break; |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1334 |
case TAG_ARG: |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1335 |
final int i = el.getArgPos(); |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1336 |
Class<?> type = ptypes[i]; |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1337 |
if (type.isPrimitive()) { |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1338 |
MethodHandle est = MethodHandles.constant(int.class, estimateSize(type)); |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1339 |
est = MethodHandles.dropArguments(est, 0, type); |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1340 |
lengthers[i] = est; |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1341 |
} else { |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1342 |
lengthers[i] = STRING_LENGTH; |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1343 |
} |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1344 |
break; |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1345 |
default: |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1346 |
throw new StringConcatException("Unhandled tag: " + el.getTag()); |
35388 | 1347 |
} |
1348 |
} |
|
1349 |
||
1350 |
// Create (StringBuilder, <args>) shape for appending: |
|
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1351 |
MethodHandle builder = MethodHandles.dropArguments(MethodHandles.identity(StringBuilder.class), 1, ptypes); |
35388 | 1352 |
|
1353 |
// Compose append calls. This is done in reverse because the application order is |
|
1354 |
// reverse as well. |
|
36737
67b2804be5ab
8152951: Avoid calculating the reverse of StringConcatFactory$Recipe elements
redestad
parents:
36668
diff
changeset
|
1355 |
List<RecipeElement> elements = recipe.getElements(); |
67b2804be5ab
8152951: Avoid calculating the reverse of StringConcatFactory$Recipe elements
redestad
parents:
36668
diff
changeset
|
1356 |
for (int i = elements.size() - 1; i >= 0; i--) { |
67b2804be5ab
8152951: Avoid calculating the reverse of StringConcatFactory$Recipe elements
redestad
parents:
36668
diff
changeset
|
1357 |
RecipeElement el = elements.get(i); |
35388 | 1358 |
MethodHandle appender; |
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1359 |
switch (el.getTag()) { |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1360 |
case TAG_CONST: |
46902
c6cefe631b18
8186500: StringConcatFactory.makeConcatWithConstants throws AssertionError when recipe contains non-String constants
redestad
parents:
42105
diff
changeset
|
1361 |
MethodHandle mh = appender(adaptToStringBuilder(String.class)); |
c6cefe631b18
8186500: StringConcatFactory.makeConcatWithConstants throws AssertionError when recipe contains non-String constants
redestad
parents:
42105
diff
changeset
|
1362 |
appender = MethodHandles.insertArguments(mh, 1, el.getValue()); |
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1363 |
break; |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1364 |
case TAG_ARG: |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1365 |
int ac = el.getArgPos(); |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1366 |
appender = appender(ptypes[ac]); |
40256
c5e03eaf7ba2
8163370: Reduce number of classes loaded by common usage of java.lang.invoke
redestad
parents:
38868
diff
changeset
|
1367 |
|
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1368 |
// Insert dummy arguments to match the prefix in the signature. |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1369 |
// The actual appender argument will be the ac-ith argument. |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1370 |
if (ac != 0) { |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1371 |
appender = MethodHandles.dropArguments(appender, 1, Arrays.copyOf(ptypes, ac)); |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1372 |
} |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1373 |
break; |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1374 |
default: |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1375 |
throw new StringConcatException("Unhandled tag: " + el.getTag()); |
35388 | 1376 |
} |
1377 |
builder = MethodHandles.foldArguments(builder, appender); |
|
1378 |
} |
|
1379 |
||
1380 |
// Build the sub-tree that adds the sizes and produces a StringBuilder: |
|
1381 |
||
1382 |
// a) Start with the reducer that accepts all arguments, plus one |
|
1383 |
// slot for the initial value. Inject the initial value right away. |
|
1384 |
// This produces (<ints>)int shape: |
|
1385 |
MethodHandle sum = getReducerFor(pc + 1); |
|
1386 |
MethodHandle adder = MethodHandles.insertArguments(sum, 0, initial); |
|
1387 |
||
1388 |
// b) Apply lengthers to transform arguments to lengths, producing (<args>)int |
|
1389 |
adder = MethodHandles.filterArguments(adder, 0, lengthers); |
|
1390 |
||
1391 |
// c) Instantiate StringBuilder (<args>)int -> (<args>)StringBuilder |
|
1392 |
MethodHandle newBuilder = MethodHandles.filterReturnValue(adder, NEW_STRING_BUILDER); |
|
1393 |
||
1394 |
// d) Fold in StringBuilder constructor, this produces (<args>)StringBuilder |
|
1395 |
MethodHandle mh = MethodHandles.foldArguments(builder, newBuilder); |
|
1396 |
||
1397 |
// Convert non-primitive arguments to Strings |
|
1398 |
mh = MethodHandles.filterArguments(mh, 0, filters); |
|
1399 |
||
1400 |
// Convert (<args>)StringBuilder to (<args>)String |
|
1401 |
if (DEBUG && mode.isExact()) { |
|
1402 |
mh = MethodHandles.filterReturnValue(mh, BUILDER_TO_STRING_CHECKED); |
|
1403 |
} else { |
|
1404 |
mh = MethodHandles.filterReturnValue(mh, BUILDER_TO_STRING); |
|
1405 |
} |
|
1406 |
||
1407 |
return mh; |
|
1408 |
} |
|
1409 |
||
1410 |
private static MethodHandle getReducerFor(int cnt) { |
|
1411 |
return SUMMERS.computeIfAbsent(cnt, SUMMER); |
|
1412 |
} |
|
1413 |
||
1414 |
private static MethodHandle appender(Class<?> appendType) { |
|
1415 |
MethodHandle appender = lookupVirtual(MethodHandles.publicLookup(), StringBuilder.class, "append", |
|
1416 |
StringBuilder.class, adaptToStringBuilder(appendType)); |
|
1417 |
||
1418 |
// appenders should return void, this would not modify the target signature during folding |
|
1419 |
MethodType nt = MethodType.methodType(void.class, StringBuilder.class, appendType); |
|
1420 |
return appender.asType(nt); |
|
1421 |
} |
|
1422 |
||
1423 |
private static String toStringChecked(StringBuilder sb) { |
|
1424 |
String s = sb.toString(); |
|
1425 |
if (s.length() != sb.capacity()) { |
|
1426 |
throw new AssertionError("Exactness check failed: result length = " + s.length() + ", buffer capacity = " + sb.capacity()); |
|
1427 |
} |
|
1428 |
return s; |
|
1429 |
} |
|
1430 |
||
1431 |
private static int sum(int v1, int v2) { |
|
1432 |
return v1 + v2; |
|
1433 |
} |
|
1434 |
||
1435 |
private static int sum(int v1, int v2, int v3) { |
|
1436 |
return v1 + v2 + v3; |
|
1437 |
} |
|
1438 |
||
1439 |
private static int sum(int v1, int v2, int v3, int v4) { |
|
1440 |
return v1 + v2 + v3 + v4; |
|
1441 |
} |
|
1442 |
||
1443 |
private static int sum(int v1, int v2, int v3, int v4, int v5) { |
|
1444 |
return v1 + v2 + v3 + v4 + v5; |
|
1445 |
} |
|
1446 |
||
1447 |
private static int sum(int v1, int v2, int v3, int v4, int v5, int v6) { |
|
1448 |
return v1 + v2 + v3 + v4 + v5 + v6; |
|
1449 |
} |
|
1450 |
||
1451 |
private static int sum(int v1, int v2, int v3, int v4, int v5, int v6, int v7) { |
|
1452 |
return v1 + v2 + v3 + v4 + v5 + v6 + v7; |
|
1453 |
} |
|
1454 |
||
1455 |
private static int sum(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8) { |
|
1456 |
return v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8; |
|
1457 |
} |
|
1458 |
||
1459 |
private static int sum(int initial, int[] vs) { |
|
1460 |
int sum = initial; |
|
1461 |
for (int v : vs) { |
|
1462 |
sum += v; |
|
1463 |
} |
|
1464 |
return sum; |
|
1465 |
} |
|
1466 |
||
1467 |
private static final ConcurrentMap<Integer, MethodHandle> SUMMERS; |
|
1468 |
||
1469 |
// This one is deliberately non-lambdified to optimize startup time: |
|
1470 |
private static final Function<Integer, MethodHandle> SUMMER = new Function<Integer, MethodHandle>() { |
|
1471 |
@Override |
|
1472 |
public MethodHandle apply(Integer cnt) { |
|
1473 |
if (cnt == 1) { |
|
1474 |
return MethodHandles.identity(int.class); |
|
1475 |
} else if (cnt <= 8) { |
|
1476 |
// Variable-arity collectors are not as efficient as small-count methods, |
|
1477 |
// unroll some initial sizes. |
|
1478 |
Class<?>[] cls = new Class<?>[cnt]; |
|
1479 |
Arrays.fill(cls, int.class); |
|
1480 |
return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, cls); |
|
1481 |
} else { |
|
1482 |
return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, int.class, int[].class) |
|
1483 |
.asCollector(int[].class, cnt - 1); |
|
1484 |
} |
|
1485 |
} |
|
1486 |
}; |
|
1487 |
||
1488 |
private static final MethodHandle NEW_STRING_BUILDER, STRING_LENGTH, BUILDER_TO_STRING, BUILDER_TO_STRING_CHECKED; |
|
1489 |
||
1490 |
static { |
|
1491 |
SUMMERS = new ConcurrentHashMap<>(); |
|
1492 |
Lookup publicLookup = MethodHandles.publicLookup(); |
|
1493 |
NEW_STRING_BUILDER = lookupConstructor(publicLookup, StringBuilder.class, int.class); |
|
1494 |
STRING_LENGTH = lookupVirtual(publicLookup, String.class, "length", int.class); |
|
1495 |
BUILDER_TO_STRING = lookupVirtual(publicLookup, StringBuilder.class, "toString", String.class); |
|
1496 |
if (DEBUG) { |
|
1497 |
BUILDER_TO_STRING_CHECKED = lookupStatic(MethodHandles.Lookup.IMPL_LOOKUP, |
|
1498 |
MethodHandleStringBuilderStrategy.class, "toStringChecked", String.class, StringBuilder.class); |
|
1499 |
} else { |
|
1500 |
BUILDER_TO_STRING_CHECKED = null; |
|
1501 |
} |
|
1502 |
} |
|
1503 |
||
1504 |
} |
|
1505 |
||
1506 |
||
1507 |
/** |
|
1508 |
* <p><b>{@link Strategy#MH_INLINE_SIZED_EXACT}: "MethodHandles inline, |
|
1509 |
* sized exactly".</b> |
|
1510 |
* |
|
1511 |
* <p>This strategy replicates what StringBuilders are doing: it builds the |
|
1512 |
* byte[] array on its own and passes that byte[] array to String |
|
1513 |
* constructor. This strategy requires access to some private APIs in JDK, |
|
1514 |
* most notably, the read-only Integer/Long.stringSize methods that measure |
|
1515 |
* the character length of the integers, and the private String constructor |
|
1516 |
* that accepts byte[] arrays without copying. While this strategy assumes a |
|
1517 |
* particular implementation details for String, this opens the door for |
|
1518 |
* building a very optimal concatenation sequence. This is the only strategy |
|
1519 |
* that requires porting if there are private JDK changes occur. |
|
1520 |
*/ |
|
1521 |
private static final class MethodHandleInlineCopyStrategy { |
|
36668
ceb837301c53
8150463: StringConcat MH_INLINE_SIZED_EXACT should skip storage initialization
shade
parents:
36511
diff
changeset
|
1522 |
static final Unsafe UNSAFE = Unsafe.getUnsafe(); |
35388 | 1523 |
|
1524 |
private MethodHandleInlineCopyStrategy() { |
|
1525 |
// no instantiation |
|
1526 |
} |
|
1527 |
||
1528 |
static MethodHandle generate(MethodType mt, Recipe recipe) throws Throwable { |
|
1529 |
||
54550
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1530 |
// Fast-path two-argument Object + Object concatenations |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1531 |
if (recipe.getElements().size() == 2) { |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1532 |
// Two object arguments |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1533 |
if (mt.parameterCount() == 2 && |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1534 |
!mt.parameterType(0).isPrimitive() && |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1535 |
!mt.parameterType(1).isPrimitive()) { |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1536 |
return SIMPLE; |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1537 |
} |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1538 |
// One element is a constant |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1539 |
if (mt.parameterCount() == 1 && !mt.parameterType(0).isPrimitive()) { |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1540 |
MethodHandle mh = SIMPLE; |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1541 |
// Insert constant element |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1542 |
|
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1543 |
// First recipe element is a constant |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1544 |
if (recipe.getElements().get(0).getTag() == TAG_CONST && |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1545 |
recipe.getElements().get(1).getTag() != TAG_CONST) { |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1546 |
return MethodHandles.insertArguments(mh, 0, |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1547 |
recipe.getElements().get(0).getValue()); |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1548 |
} else if (recipe.getElements().get(1).getTag() == TAG_CONST && |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1549 |
recipe.getElements().get(0).getTag() != TAG_CONST) { |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1550 |
return MethodHandles.insertArguments(mh, 1, |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1551 |
recipe.getElements().get(1).getValue()); |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1552 |
} |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1553 |
// else... fall-through to slow-path |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1554 |
} |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1555 |
} |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1556 |
|
35388 | 1557 |
// Create filters and obtain filtered parameter types. Filters would be used in the beginning |
1558 |
// to convert the incoming arguments into the arguments we can process (e.g. Objects -> Strings). |
|
1559 |
// The filtered argument type list is used all over in the combinators below. |
|
1560 |
Class<?>[] ptypes = mt.parameterArray(); |
|
1561 |
MethodHandle[] filters = null; |
|
1562 |
for (int i = 0; i < ptypes.length; i++) { |
|
1563 |
MethodHandle filter = Stringifiers.forMost(ptypes[i]); |
|
1564 |
if (filter != null) { |
|
1565 |
if (filters == null) { |
|
1566 |
filters = new MethodHandle[ptypes.length]; |
|
1567 |
} |
|
1568 |
filters[i] = filter; |
|
1569 |
ptypes[i] = filter.type().returnType(); |
|
1570 |
} |
|
1571 |
} |
|
1572 |
||
1573 |
// Start building the combinator tree. The tree "starts" with (<parameters>)String, and "finishes" |
|
52326
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1574 |
// with the (byte[], long)String shape to invoke newString in StringConcatHelper. The combinators are |
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1575 |
// assembled bottom-up, which makes the code arguably hard to read. |
35388 | 1576 |
|
1577 |
// Drop all remaining parameter types, leave only helper arguments: |
|
1578 |
MethodHandle mh; |
|
1579 |
||
52326
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1580 |
mh = MethodHandles.dropArguments(NEW_STRING, 2, ptypes); |
35388 | 1581 |
|
52326
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1582 |
// Mix in prependers. This happens when (byte[], long) = (storage, indexCoder) is already |
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1583 |
// known from the combinators below. We are assembling the string backwards, so the index coded |
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1584 |
// into indexCoder is the *ending* index. |
35388 | 1585 |
for (RecipeElement el : recipe.getElements()) { |
40810
b88d5910ea1e
8165492: Reduce number of lambda forms generated by MethodHandleInlineCopyStrategy
redestad
parents:
40272
diff
changeset
|
1586 |
// Do the prepend, and put "new" index at index 1 |
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1587 |
switch (el.getTag()) { |
40810
b88d5910ea1e
8165492: Reduce number of lambda forms generated by MethodHandleInlineCopyStrategy
redestad
parents:
40272
diff
changeset
|
1588 |
case TAG_CONST: { |
52326
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1589 |
MethodHandle prepender = MethodHandles.insertArguments(prepender(String.class), 2, el.getValue()); |
52226
b4b932c6001f
8212726: Replace some use of drop- and foldArguments with filtering argument combinator in StringConcatFactory
redestad
parents:
52177
diff
changeset
|
1590 |
mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepender, |
52326
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1591 |
1, 0 // indexCoder, storage |
40810
b88d5910ea1e
8165492: Reduce number of lambda forms generated by MethodHandleInlineCopyStrategy
redestad
parents:
40272
diff
changeset
|
1592 |
); |
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1593 |
break; |
40810
b88d5910ea1e
8165492: Reduce number of lambda forms generated by MethodHandleInlineCopyStrategy
redestad
parents:
40272
diff
changeset
|
1594 |
} |
b88d5910ea1e
8165492: Reduce number of lambda forms generated by MethodHandleInlineCopyStrategy
redestad
parents:
40272
diff
changeset
|
1595 |
case TAG_ARG: { |
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1596 |
int pos = el.getArgPos(); |
40810
b88d5910ea1e
8165492: Reduce number of lambda forms generated by MethodHandleInlineCopyStrategy
redestad
parents:
40272
diff
changeset
|
1597 |
MethodHandle prepender = prepender(ptypes[pos]); |
52226
b4b932c6001f
8212726: Replace some use of drop- and foldArguments with filtering argument combinator in StringConcatFactory
redestad
parents:
52177
diff
changeset
|
1598 |
mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepender, |
52326
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1599 |
1, 0, // indexCoder, storage |
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1600 |
2 + pos // selected argument |
40810
b88d5910ea1e
8165492: Reduce number of lambda forms generated by MethodHandleInlineCopyStrategy
redestad
parents:
40272
diff
changeset
|
1601 |
); |
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1602 |
break; |
40810
b88d5910ea1e
8165492: Reduce number of lambda forms generated by MethodHandleInlineCopyStrategy
redestad
parents:
40272
diff
changeset
|
1603 |
} |
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1604 |
default: |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1605 |
throw new StringConcatException("Unhandled tag: " + el.getTag()); |
35388 | 1606 |
} |
1607 |
} |
|
1608 |
||
40810
b88d5910ea1e
8165492: Reduce number of lambda forms generated by MethodHandleInlineCopyStrategy
redestad
parents:
40272
diff
changeset
|
1609 |
// Fold in byte[] instantiation at argument 0 |
52226
b4b932c6001f
8212726: Replace some use of drop- and foldArguments with filtering argument combinator in StringConcatFactory
redestad
parents:
52177
diff
changeset
|
1610 |
mh = MethodHandles.foldArgumentsWithCombiner(mh, 0, NEW_ARRAY, |
52326
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1611 |
1 // index |
40810
b88d5910ea1e
8165492: Reduce number of lambda forms generated by MethodHandleInlineCopyStrategy
redestad
parents:
40272
diff
changeset
|
1612 |
); |
35388 | 1613 |
|
1614 |
// Start combining length and coder mixers. |
|
1615 |
// |
|
1616 |
// Length is easy: constant lengths can be computed on the spot, and all non-constant |
|
1617 |
// shapes have been either converted to Strings, or explicit methods for getting the |
|
1618 |
// string length out of primitives are provided. |
|
1619 |
// |
|
1620 |
// Coders are more interesting. Only Object, String and char arguments (and constants) |
|
1621 |
// can have non-Latin1 encoding. It is easier to blindly convert constants to String, |
|
1622 |
// and deduce the coder from there. Arguments would be either converted to Strings |
|
52326
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1623 |
// during the initial filtering, or handled by specializations in MIXERS. |
35388 | 1624 |
// |
52326
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1625 |
// The method handle shape before and after all mixers are combined in is: |
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1626 |
// (long, <args>)String = ("indexCoder", <args>) |
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1627 |
long initialLengthCoder = INITIAL_CODER; |
35388 | 1628 |
for (RecipeElement el : recipe.getElements()) { |
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1629 |
switch (el.getTag()) { |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1630 |
case TAG_CONST: |
46902
c6cefe631b18
8186500: StringConcatFactory.makeConcatWithConstants throws AssertionError when recipe contains non-String constants
redestad
parents:
42105
diff
changeset
|
1631 |
String constant = el.getValue(); |
52326
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1632 |
initialLengthCoder = (long)mixer(String.class).invoke(initialLengthCoder, constant); |
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1633 |
break; |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1634 |
case TAG_ARG: |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1635 |
int ac = el.getArgPos(); |
35388 | 1636 |
|
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1637 |
Class<?> argClass = ptypes[ac]; |
52326
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1638 |
MethodHandle mix = mixer(argClass); |
35388 | 1639 |
|
52326
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1640 |
// Compute new "index" in-place using old value plus the appropriate argument. |
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1641 |
mh = MethodHandles.filterArgumentsWithCombiner(mh, 0, mix, |
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1642 |
0, // old-index |
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1643 |
1 + ac // selected argument |
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1644 |
); |
52177
430e6421d503
8212597: Optimize String concatenation setup when using primitive operands
redestad
parents:
48930
diff
changeset
|
1645 |
|
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1646 |
break; |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1647 |
default: |
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1648 |
throw new StringConcatException("Unhandled tag: " + el.getTag()); |
35388 | 1649 |
} |
1650 |
} |
|
1651 |
||
52326
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1652 |
// Insert initial length and coder value here. |
35388 | 1653 |
// The method handle shape here is (<args>). |
52326
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1654 |
mh = MethodHandles.insertArguments(mh, 0, initialLengthCoder); |
35388 | 1655 |
|
1656 |
// Apply filters, converting the arguments: |
|
1657 |
if (filters != null) { |
|
1658 |
mh = MethodHandles.filterArguments(mh, 0, filters); |
|
1659 |
} |
|
1660 |
||
1661 |
return mh; |
|
1662 |
} |
|
1663 |
||
1664 |
private static MethodHandle prepender(Class<?> cl) { |
|
1665 |
return PREPENDERS.computeIfAbsent(cl, PREPEND); |
|
1666 |
} |
|
1667 |
||
52326
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1668 |
private static MethodHandle mixer(Class<?> cl) { |
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1669 |
return MIXERS.computeIfAbsent(cl, MIX); |
35388 | 1670 |
} |
1671 |
||
1672 |
// This one is deliberately non-lambdified to optimize startup time: |
|
1673 |
private static final Function<Class<?>, MethodHandle> PREPEND = new Function<Class<?>, MethodHandle>() { |
|
1674 |
@Override |
|
1675 |
public MethodHandle apply(Class<?> c) { |
|
52326
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1676 |
return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", long.class, long.class, byte[].class, |
46902
c6cefe631b18
8186500: StringConcatFactory.makeConcatWithConstants throws AssertionError when recipe contains non-String constants
redestad
parents:
42105
diff
changeset
|
1677 |
Wrapper.asPrimitiveType(c)); |
35388 | 1678 |
} |
1679 |
}; |
|
1680 |
||
1681 |
// This one is deliberately non-lambdified to optimize startup time: |
|
52326
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1682 |
private static final Function<Class<?>, MethodHandle> MIX = new Function<Class<?>, MethodHandle>() { |
35388 | 1683 |
@Override |
1684 |
public MethodHandle apply(Class<?> c) { |
|
52326
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1685 |
return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mix", long.class, long.class, |
46902
c6cefe631b18
8186500: StringConcatFactory.makeConcatWithConstants throws AssertionError when recipe contains non-String constants
redestad
parents:
42105
diff
changeset
|
1686 |
Wrapper.asPrimitiveType(c)); |
35388 | 1687 |
} |
1688 |
}; |
|
1689 |
||
54550
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1690 |
private static final MethodHandle SIMPLE; |
35388 | 1691 |
private static final MethodHandle NEW_STRING; |
1692 |
private static final MethodHandle NEW_ARRAY; |
|
1693 |
private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS; |
|
52326
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1694 |
private static final ConcurrentMap<Class<?>, MethodHandle> MIXERS; |
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1695 |
private static final long INITIAL_CODER; |
35388 | 1696 |
|
1697 |
static { |
|
1698 |
try { |
|
52326
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1699 |
MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", long.class); |
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1700 |
INITIAL_CODER = (long) initCoder.invoke(); |
35700
b933119b8f84
8148869: StringConcatFactory MH_INLINE_SIZED_EXACT strategy does not work with -XX:-CompactStrings
shade
parents:
35639
diff
changeset
|
1701 |
} catch (Throwable e) { |
35388 | 1702 |
throw new AssertionError(e); |
1703 |
} |
|
1704 |
||
1705 |
PREPENDERS = new ConcurrentHashMap<>(); |
|
52326
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1706 |
MIXERS = new ConcurrentHashMap<>(); |
35388 | 1707 |
|
54550
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1708 |
SIMPLE = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "simpleConcat", String.class, Object.class, Object.class); |
52326
77018c2b97df
8213035: Pack MethodHandleInlineStrategy coder and length into a long
redestad
parents:
52226
diff
changeset
|
1709 |
NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, long.class); |
54550
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1710 |
NEW_ARRAY = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newArray", byte[].class, long.class); |
35388 | 1711 |
} |
1712 |
} |
|
1713 |
||
1714 |
/** |
|
1715 |
* Public gateways to public "stringify" methods. These methods have the form String apply(T obj), and normally |
|
1716 |
* delegate to {@code String.valueOf}, depending on argument's type. |
|
1717 |
*/ |
|
1718 |
private static final class Stringifiers { |
|
1719 |
private Stringifiers() { |
|
1720 |
// no instantiation |
|
1721 |
} |
|
1722 |
||
54550
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1723 |
private static final MethodHandle OBJECT_INSTANCE = |
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1724 |
lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "stringOf", String.class, Object.class); |
35388 | 1725 |
|
52497
34510f65fb58
8213741: Consolidate Object and String Stringifiers
redestad
parents:
52326
diff
changeset
|
1726 |
private static class FloatStringifiers { |
34510f65fb58
8213741: Consolidate Object and String Stringifiers
redestad
parents:
52326
diff
changeset
|
1727 |
private static final MethodHandle FLOAT_INSTANCE = |
34510f65fb58
8213741: Consolidate Object and String Stringifiers
redestad
parents:
52326
diff
changeset
|
1728 |
lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, float.class); |
34510f65fb58
8213741: Consolidate Object and String Stringifiers
redestad
parents:
52326
diff
changeset
|
1729 |
|
34510f65fb58
8213741: Consolidate Object and String Stringifiers
redestad
parents:
52326
diff
changeset
|
1730 |
private static final MethodHandle DOUBLE_INSTANCE = |
34510f65fb58
8213741: Consolidate Object and String Stringifiers
redestad
parents:
52326
diff
changeset
|
1731 |
lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, double.class); |
38868
f353ab3063f8
8158933: String concat stringifiers setup should avoid unnecessary lookups
redestad
parents:
38428
diff
changeset
|
1732 |
} |
35388 | 1733 |
|
38868
f353ab3063f8
8158933: String concat stringifiers setup should avoid unnecessary lookups
redestad
parents:
38428
diff
changeset
|
1734 |
private static class StringifierAny extends ClassValue<MethodHandle> { |
52497
34510f65fb58
8213741: Consolidate Object and String Stringifiers
redestad
parents:
52326
diff
changeset
|
1735 |
|
34510f65fb58
8213741: Consolidate Object and String Stringifiers
redestad
parents:
52326
diff
changeset
|
1736 |
private static final ClassValue<MethodHandle> INSTANCE = new StringifierAny(); |
34510f65fb58
8213741: Consolidate Object and String Stringifiers
redestad
parents:
52326
diff
changeset
|
1737 |
|
35388 | 1738 |
@Override |
38868
f353ab3063f8
8158933: String concat stringifiers setup should avoid unnecessary lookups
redestad
parents:
38428
diff
changeset
|
1739 |
protected MethodHandle computeValue(Class<?> cl) { |
35388 | 1740 |
if (cl == byte.class || cl == short.class || cl == int.class) { |
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
1741 |
return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, int.class); |
35388 | 1742 |
} else if (cl == boolean.class) { |
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
1743 |
return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, boolean.class); |
35388 | 1744 |
} else if (cl == char.class) { |
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
1745 |
return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, char.class); |
35388 | 1746 |
} else if (cl == long.class) { |
36001
5f0acf0668e0
8149835: StringConcatFactory should emit classes with the same package as the host class
shade
parents:
35776
diff
changeset
|
1747 |
return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, long.class); |
35388 | 1748 |
} else { |
52497
34510f65fb58
8213741: Consolidate Object and String Stringifiers
redestad
parents:
52326
diff
changeset
|
1749 |
MethodHandle mh = forMost(cl); |
38868
f353ab3063f8
8158933: String concat stringifiers setup should avoid unnecessary lookups
redestad
parents:
38428
diff
changeset
|
1750 |
if (mh != null) { |
f353ab3063f8
8158933: String concat stringifiers setup should avoid unnecessary lookups
redestad
parents:
38428
diff
changeset
|
1751 |
return mh; |
f353ab3063f8
8158933: String concat stringifiers setup should avoid unnecessary lookups
redestad
parents:
38428
diff
changeset
|
1752 |
} else { |
f353ab3063f8
8158933: String concat stringifiers setup should avoid unnecessary lookups
redestad
parents:
38428
diff
changeset
|
1753 |
throw new IllegalStateException("Unknown class: " + cl); |
f353ab3063f8
8158933: String concat stringifiers setup should avoid unnecessary lookups
redestad
parents:
38428
diff
changeset
|
1754 |
} |
35388 | 1755 |
} |
1756 |
} |
|
38868
f353ab3063f8
8158933: String concat stringifiers setup should avoid unnecessary lookups
redestad
parents:
38428
diff
changeset
|
1757 |
} |
35388 | 1758 |
|
1759 |
/** |
|
1760 |
* Returns a stringifier for references and floats/doubles only. |
|
1761 |
* Always returns null for other primitives. |
|
1762 |
* |
|
1763 |
* @param t class to stringify |
|
1764 |
* @return stringifier; null, if not available |
|
1765 |
*/ |
|
1766 |
static MethodHandle forMost(Class<?> t) { |
|
52497
34510f65fb58
8213741: Consolidate Object and String Stringifiers
redestad
parents:
52326
diff
changeset
|
1767 |
if (!t.isPrimitive()) { |
54550
5fa7fbddfe9d
8222484: Specialize generation of simple String concatenation expressions
redestad
parents:
52497
diff
changeset
|
1768 |
return OBJECT_INSTANCE; |
52497
34510f65fb58
8213741: Consolidate Object and String Stringifiers
redestad
parents:
52326
diff
changeset
|
1769 |
} else if (t == float.class) { |
34510f65fb58
8213741: Consolidate Object and String Stringifiers
redestad
parents:
52326
diff
changeset
|
1770 |
return FloatStringifiers.FLOAT_INSTANCE; |
34510f65fb58
8213741: Consolidate Object and String Stringifiers
redestad
parents:
52326
diff
changeset
|
1771 |
} else if (t == double.class) { |
34510f65fb58
8213741: Consolidate Object and String Stringifiers
redestad
parents:
52326
diff
changeset
|
1772 |
return FloatStringifiers.DOUBLE_INSTANCE; |
34510f65fb58
8213741: Consolidate Object and String Stringifiers
redestad
parents:
52326
diff
changeset
|
1773 |
} |
34510f65fb58
8213741: Consolidate Object and String Stringifiers
redestad
parents:
52326
diff
changeset
|
1774 |
return null; |
35388 | 1775 |
} |
1776 |
||
1777 |
/** |
|
1778 |
* Returns a stringifier for any type. Never returns null. |
|
1779 |
* |
|
1780 |
* @param t class to stringify |
|
1781 |
* @return stringifier |
|
1782 |
*/ |
|
1783 |
static MethodHandle forAny(Class<?> t) { |
|
52497
34510f65fb58
8213741: Consolidate Object and String Stringifiers
redestad
parents:
52326
diff
changeset
|
1784 |
return StringifierAny.INSTANCE.get(t); |
35388 | 1785 |
} |
1786 |
} |
|
1787 |
||
1788 |
/* ------------------------------- Common utilities ------------------------------------ */ |
|
1789 |
||
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1790 |
static MethodHandle lookupStatic(Lookup lookup, Class<?> refc, String name, Class<?> rtype, Class<?>... ptypes) { |
35388 | 1791 |
try { |
1792 |
return lookup.findStatic(refc, name, MethodType.methodType(rtype, ptypes)); |
|
1793 |
} catch (NoSuchMethodException | IllegalAccessException e) { |
|
1794 |
throw new AssertionError(e); |
|
1795 |
} |
|
1796 |
} |
|
1797 |
||
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1798 |
static MethodHandle lookupVirtual(Lookup lookup, Class<?> refc, String name, Class<?> rtype, Class<?>... ptypes) { |
35388 | 1799 |
try { |
1800 |
return lookup.findVirtual(refc, name, MethodType.methodType(rtype, ptypes)); |
|
1801 |
} catch (NoSuchMethodException | IllegalAccessException e) { |
|
1802 |
throw new AssertionError(e); |
|
1803 |
} |
|
1804 |
} |
|
1805 |
||
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1806 |
static MethodHandle lookupConstructor(Lookup lookup, Class<?> refc, Class<?> ptypes) { |
35388 | 1807 |
try { |
1808 |
return lookup.findConstructor(refc, MethodType.methodType(void.class, ptypes)); |
|
1809 |
} catch (NoSuchMethodException | IllegalAccessException e) { |
|
1810 |
throw new AssertionError(e); |
|
1811 |
} |
|
1812 |
} |
|
1813 |
||
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1814 |
static int estimateSize(Class<?> cl) { |
35388 | 1815 |
if (cl == Integer.TYPE) { |
1816 |
return 11; // "-2147483648" |
|
1817 |
} else if (cl == Boolean.TYPE) { |
|
1818 |
return 5; // "false" |
|
1819 |
} else if (cl == Byte.TYPE) { |
|
1820 |
return 4; // "-128" |
|
1821 |
} else if (cl == Character.TYPE) { |
|
1822 |
return 1; // duh |
|
1823 |
} else if (cl == Short.TYPE) { |
|
1824 |
return 6; // "-32768" |
|
1825 |
} else if (cl == Double.TYPE) { |
|
1826 |
return 26; // apparently, no larger than this, see FloatingDecimal.BinaryToASCIIBuffer.buffer |
|
1827 |
} else if (cl == Float.TYPE) { |
|
1828 |
return 26; // apparently, no larger than this, see FloatingDecimal.BinaryToASCIIBuffer.buffer |
|
1829 |
} else if (cl == Long.TYPE) { |
|
1830 |
return 20; // "-9223372036854775808" |
|
1831 |
} else { |
|
1832 |
throw new IllegalArgumentException("Cannot estimate the size for " + cl); |
|
1833 |
} |
|
1834 |
} |
|
1835 |
||
40272
6af4511ee5a4
8163878: Remove unnecessary bridge methods, allocations in java.lang.invoke
redestad
parents:
40256
diff
changeset
|
1836 |
static Class<?> adaptToStringBuilder(Class<?> c) { |
35388 | 1837 |
if (c.isPrimitive()) { |
1838 |
if (c == Byte.TYPE || c == Short.TYPE) { |
|
1839 |
return int.class; |
|
1840 |
} |
|
1841 |
} else { |
|
1842 |
if (c != String.class) { |
|
1843 |
return Object.class; |
|
1844 |
} |
|
1845 |
} |
|
1846 |
return c; |
|
1847 |
} |
|
1848 |
||
1849 |
private StringConcatFactory() { |
|
1850 |
// no instantiation |
|
1851 |
} |
|
1852 |
||
1853 |
} |