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