1 /*
2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. 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 jdk.experimental.value;
27
28 import jdk.experimental.bytecode.BasicClassBuilder.BasicPoolHelper;
29 import jdk.experimental.bytecode.BasicClassBuilder.BasicTypeHelper;
30 import jdk.experimental.bytecode.ClassBuilder;
31 import jdk.experimental.bytecode.CodeBuilder;
32 import jdk.experimental.bytecode.Flag;
33 import jdk.experimental.bytecode.MethodBuilder;
34 import jdk.experimental.bytecode.PoolHelper;
35 import jdk.experimental.bytecode.TypeHelper;
36 import jdk.experimental.bytecode.TypeTag;
37 import jdk.experimental.bytecode.TypedCodeBuilder;
38 import jdk.experimental.value.MethodHandleBuilder.IsolatedMethodBuilder.IsolatedMethodPoolHelper;
39 import jdk.internal.misc.Unsafe;
40 import sun.security.action.GetPropertyAction;
41 import valhalla.shady.MinimalValueTypes_1_0;
42
43 import java.lang.invoke.MethodHandle;
44 import java.lang.invoke.MethodHandles.Lookup;
45 import java.lang.invoke.MethodType;
46 import java.util.*;
47 import java.util.function.Consumer;
48 import java.util.function.Function;
49
50 /**
51 * Utility class for building method handles.
52 */
53 public class MethodHandleBuilder {
54
55 static final Unsafe UNSAFE = Unsafe.getUnsafe();
56
57 static final boolean ENABLE_POOL_PATCHES;
58
59 static {
60 Properties props = GetPropertyAction.privilegedGetProperties();
61 ENABLE_POOL_PATCHES = Boolean.parseBoolean(
62 props.getProperty("valhalla.enablePoolPatches"));
63 }
64
65 public static MethodHandle loadCode(Lookup lookup, String name, MethodType type, Consumer<? super MethodHandleCodeBuilder<?>> builder) {
66 return loadCode(lookup, name, type.toMethodDescriptorString(), builder);
67 }
68
69 public static MethodHandle loadCode(Lookup lookup, String name, String type, Consumer<? super MethodHandleCodeBuilder<?>> builder) {
70 return loadCode(lookup, name, name, type, MethodHandleCodeBuilder::new,
71 clazz -> {
72 try {
73 return lookup.findStatic(clazz, name, MethodType.fromMethodDescriptorString(type, lookup.lookupClass().getClassLoader()));
74 } catch (ReflectiveOperationException ex) {
75 throw new IllegalStateException(ex);
76 }
77 },
78 builder);
79 }
80
81 protected static <Z, C extends CodeBuilder<Class<?>, String, byte[], ?>> Z loadCode(
82 Lookup lookup, String className, String methodName, String type,
83 Function<MethodBuilder<Class<?>, String, byte[]>, ? extends C> builderFunc,
84 Function<Class<?>, Z> resFunc, Consumer<? super C> builder) {
85
86 IsolatedMethodBuilder isolatedMethodBuilder = new IsolatedMethodBuilder(className, lookup);
87 isolatedMethodBuilder
88 .withSuperclass(Object.class)
89 .withMajorVersion(52)
90 .withMinorVersion(0)
91 .withFlags(Flag.ACC_PUBLIC)
92 .withMethod(methodName, type, M ->
93 M.withFlags(Flag.ACC_STATIC, Flag.ACC_PUBLIC)
94 .withCode(builderFunc, builder));
95
96 try {
97 byte[] barr = isolatedMethodBuilder.build();
98 MinimalValueTypes_1_0.maybeDump(className, barr.clone());
99 Class<?> clazz = UNSAFE.defineAnonymousClass(lookup.lookupClass(), barr, isolatedMethodBuilder.patches());
100 UNSAFE.ensureClassInitialized(clazz);
101 return resFunc.apply(clazz);
102 } catch (Throwable e) {
103 throw new IllegalStateException(e);
104 }
105 }
106
107 static class IsolatedMethodBuilder extends ClassBuilder<Class<?>, String, IsolatedMethodBuilder> {
108
109 private static final Class<?> THIS_CLASS = new Object() { }.getClass();
110
111 IsolatedMethodBuilder(String clazz, Lookup lookup) {
112 super(ENABLE_POOL_PATCHES ?
113 new IsolatedMethodPatchingPoolHelper(clazz) :
114 new IsolatedMethodPoolHelper(clazz),
115 new IsolatedMethodTypeHelper(lookup));
116 withThisClass(THIS_CLASS);
117 }
118
119 Object[] patches() {
120 return ((IsolatedMethodPoolHelper)poolHelper).patches();
121 }
122
123 static class IsolatedMethodTypeHelper implements TypeHelper<Class<?>, String> {
124
125 BasicTypeHelper basicTypeHelper = new BasicTypeHelper();
126 Lookup lookup;
127
128 IsolatedMethodTypeHelper(Lookup lookup) {
129 this.lookup = lookup;
130 }
131
132 @Override
133 public String elemtype(String s) {
134 return basicTypeHelper.elemtype(s);
135 }
136
137 @Override
138 public String arrayOf(String s) {
139 return basicTypeHelper.arrayOf(s);
140 }
141
142 @Override
143 public Iterator<String> parameterTypes(String s) {
144 return basicTypeHelper.parameterTypes(s);
145 }
146
147 @Override
148 public String fromTag(TypeTag tag) {
149 return basicTypeHelper.fromTag(tag);
150 }
151
152 @Override
153 public String returnType(String s) {
154 return basicTypeHelper.returnType(s);
155 }
156
157 @Override
158 public String type(Class<?> aClass) {
159 if (aClass.isArray()) {
160 return aClass.getName().replaceAll("\\.", "/");
161 } else {
162 return MinimalValueTypes_1_0.isValueType(aClass) ?
163 "Q" + aClass.getName().replaceAll("\\.", "/") + ";" :
164 "L" + aClass.getName().replaceAll("\\.", "/") + ";";
165 }
166 }
167
168 @Override
169 public Class<?> symbol(String desc) {
170 try {
171 if (desc.startsWith("[")) {
172 return Class.forName(desc.replaceAll("/", "."), true, lookup.lookupClass().getClassLoader());
173 } else {
174 return Class.forName(basicTypeHelper.symbol(desc).replaceAll("/", "."), true, lookup.lookupClass().getClassLoader());
175 }
176 } catch (ReflectiveOperationException ex) {
177 throw new AssertionError(ex);
178 }
179 }
180
181 @Override
182 public TypeTag tag(String s) {
183 return basicTypeHelper.tag(s);
184 }
185
186 @Override
187 public Class<?> symbolFrom(String s) {
188 return symbol(s);
189 }
190
191 @Override
192 public String commonSupertype(String t1, String t2) {
193 return basicTypeHelper.commonSupertype(t1, t2);
194 }
195
196 @Override
197 public String nullType() {
198 return basicTypeHelper.nullType();
199 }
200 }
201
202 static class IsolatedMethodPoolHelper implements PoolHelper<Class<?>, String, byte[]> {
203 BasicPoolHelper basicPoolHelper = new BasicPoolHelper();
204 String clazz;
205
206 IsolatedMethodPoolHelper(String clazz) {
207 this.clazz = clazz;
208 }
209
210 String from(Class<?> c) {
211 String name;
212 boolean isValue = MinimalValueTypes_1_0.isValueType(c);
213 if (c == THIS_CLASS) {
214 //THIS_CLASS cannot be a DVT (by construction) - never mangle
215 name = clazz;
216 } else {
217 name = isValue ?
218 MinimalValueTypes_1_0.mangleValueClassName(c.getName()) :
219 c.getName();
220 }
221 return name.replaceAll("\\.", "/");
222 }
223
224 @Override
225 public int putClass(Class<?> symbol) {
226 return basicPoolHelper.putClass(from(symbol));
227 }
228
229 @Override
230 public int putFieldRef(Class<?> owner, CharSequence name, String type) {
231 return basicPoolHelper.putFieldRef(from(owner), name, type);
232 }
233
234 @Override
235 public int putMethodRef(Class<?> owner, CharSequence name, String type, boolean isInterface) {
236 return basicPoolHelper.putMethodRef(from(owner), name, type, isInterface);
237 }
238
239 @Override
240 public int putUtf8(CharSequence s) {
241 return basicPoolHelper.putUtf8(s);
242 }
243
244 @Override
245 public int putType(String s) {
246 return basicPoolHelper.putType(s);
247 }
248
249 @Override
250 public int putValue(Object v) {
251 return basicPoolHelper.putValue(v);
252 }
253
254 @Override
255 public int putMethodType(String s) {
256 return basicPoolHelper.putMethodType(s);
257 }
258
259 @Override
260 public int putHandle(int refKind, Class<?> owner, CharSequence name, String type) {
261 return basicPoolHelper.putHandle(refKind, from(owner), name, type);
262 }
263
264 @Override
265 public int putInvokeDynamic(CharSequence invokedName, String invokedType, int bskKind, Class<?> bsmClass, CharSequence bsmName, String bsmType, Object... staticArgs) {
266 return basicPoolHelper.putInvokeDynamic(invokedName, invokedType, bskKind, from(bsmClass), bsmName, bsmType, staticArgs);
267 }
268
269 @Override
270 public int size() {
271 return basicPoolHelper.size();
272 }
273
274 @Override
275 public byte[] entries() {
276 return basicPoolHelper.entries();
277 }
278
279 Object[] patches() {
280 return null;
281 }
282 }
283
284 @Override
285 public byte[] build() {
286 return super.build();
287 }
288 }
289
290 static class IsolatedMethodPatchingPoolHelper extends IsolatedMethodPoolHelper {
291
292 public IsolatedMethodPatchingPoolHelper(String clazz) {
293 super(clazz);
294 }
295
296 Map<Object, CpPatch> cpPatches = new HashMap<>();
297 int cph = 0; // for counting constant placeholders
298
299 static class CpPatch {
300
301 final int index;
302 final String placeholder;
303 final Object value;
304
305 CpPatch(int index, String placeholder, Object value) {
306 this.index = index;
307 this.placeholder = placeholder;
308 this.value = value;
309 }
310
311 public String toString() {
312 return "CpPatch/index="+index+",placeholder="+placeholder+",value="+value;
313 }
314 }
315
316 @Override
317 public int putValue(Object v) {
318 if (v instanceof String || v instanceof Integer || v instanceof Float || v instanceof Double || v instanceof Long) {
319 return basicPoolHelper.putValue(v);
320 }
321 assert (!v.getClass().isPrimitive()) : v;
322 return patchPoolEntry(v); // CP patching support
323 }
324
325 int patchPoolEntry(Object v) {
326 String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + cph++;
327 if (MinimalValueTypes_1_0.DUMP_CLASS_FILES) cpPlaceholder += " <<" + debugString(v) + ">>";
328 if (cpPatches.containsKey(cpPlaceholder)) {
329 throw new InternalError("observed CP placeholder twice: " + cpPlaceholder);
330 }
331 // insert placeholder in CP and remember the patch
332 int index = basicPoolHelper.putValue(cpPlaceholder); // TODO check if already in the constant pool
333 cpPatches.put(cpPlaceholder, new CpPatch(index, cpPlaceholder, v));
334 return index;
335 }
336
337 @Override
338 Object[] patches() {
339 int size = size();
340 Object[] res = new Object[size];
341 for (CpPatch p : cpPatches.values()) {
342 if (p.index >= size)
343 throw new InternalError("bad cp patch index");
344 res[p.index] = p.value;
345 }
346 return res;
347 }
348
349 private static String debugString(Object arg) {
350 // @@@ Cannot crack open a MH like with InvokerByteCodeGenerator.debugString
351 return arg.toString();
352 }
353 }
354
355 public static class MethodHandleCodeBuilder<T extends MethodHandleCodeBuilder<T>> extends TypedCodeBuilder<Class<?>, String, byte[], T> {
356
357 BasicTypeHelper basicTypeHelper = new BasicTypeHelper();
358
359 public MethodHandleCodeBuilder(jdk.experimental.bytecode.MethodBuilder<Class<?>, String, byte[]> methodBuilder) {
360 super(methodBuilder);
361 }
362
363 TypeTag getTagType(String s) {
364 return basicTypeHelper.tag(s);
365 }
366
367 public T ifcmp(String s, CondKind cond, CharSequence label) {
368 return super.ifcmp(getTagType(s), cond, label);
369 }
370
371 public T return_(String s) {
372 return super.return_(getTagType(s));
373 }
374 }
375 }
--- EOF ---