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.BytePoolHelper; 29 import jdk.experimental.bytecode.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.TypeHelper; 35 import jdk.experimental.bytecode.TypeTag; 36 import jdk.experimental.bytecode.TypedCodeBuilder; 37 import jdk.experimental.value.MethodHandleBuilder.IsolatedMethodBuilder.IsolatedMethodPoolHelper; 38 import jdk.internal.misc.Unsafe; 39 import sun.security.action.GetPropertyAction; 40 41 import java.io.IOException; 42 import java.io.OutputStream; 43 import java.io.UncheckedIOException; 44 import java.lang.invoke.MethodHandle; 45 import java.lang.invoke.MethodHandles.Lookup; 46 import java.lang.invoke.MethodType; 47 import java.nio.file.Files; 48 import java.nio.file.Path; 49 import java.nio.file.Paths; 50 import java.util.*; 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 public static final boolean ENABLE_POOL_PATCHES; 62 public static final String DUMP_CLASS_FILES_DIR; 63 64 static { 65 Properties props = GetPropertyAction.privilegedGetProperties(); 66 ENABLE_POOL_PATCHES = Boolean.parseBoolean( 67 props.getProperty("valhalla.enablePoolPatches")); 68 DUMP_CLASS_FILES_DIR = props.getProperty("valhalla.DUMP_CLASS_FILES_DIR"); 69 } 70 71 public static MethodHandle loadCode(Lookup lookup, String name, MethodType type, Consumer<? super MethodHandleCodeBuilder<?>> builder) { 72 return loadCode(lookup, name, name, type, builder); 73 } 74 75 public static MethodHandle loadCode(Lookup lookup, String className, String methodName, MethodType type, Consumer<? super MethodHandleCodeBuilder<?>> builder) { 76 String descriptor = type.toMethodDescriptorString(); 77 return loadCode(lookup, className, methodName, descriptor, MethodHandleCodeBuilder::new, 78 clazz -> { 79 try { 80 return lookup.findStatic(clazz, methodName, MethodType.fromMethodDescriptorString(descriptor, lookup.lookupClass().getClassLoader())); 81 } catch (ReflectiveOperationException ex) { 82 throw new IllegalStateException(ex); 83 } 84 }, 85 builder); 86 } 87 88 protected static <Z, C extends CodeBuilder<Class<?>, String, byte[], ?>> Z loadCode( 89 Lookup lookup, String className, String methodName, String type, 90 Function<MethodBuilder<Class<?>, String, byte[]>, ? extends C> builderFunc, 91 Function<Class<?>, Z> resFunc, Consumer<? super C> builder) { 92 93 IsolatedMethodBuilder isolatedMethodBuilder = new IsolatedMethodBuilder(className, lookup); 94 isolatedMethodBuilder 95 .withSuperclass(Object.class) 96 .withMajorVersion(57) 97 .withMinorVersion(0) 98 .withFlags(Flag.ACC_PUBLIC) 99 .withMethod(methodName, type, M -> 100 M.withFlags(Flag.ACC_STATIC, Flag.ACC_PUBLIC) 101 .withCode(builderFunc, builder)); 102 103 try { 104 byte[] barr = isolatedMethodBuilder.build(); 105 maybeDump(className, barr); 106 Class<?> clazz = UNSAFE.defineAnonymousClass(lookup.lookupClass(), barr, isolatedMethodBuilder.patches()); 107 UNSAFE.ensureClassInitialized(clazz); 108 return resFunc.apply(clazz); 109 } catch (Throwable e) { 110 throw new IllegalStateException(e); 111 } 112 } 113 114 public static class IsolatedMethodBuilder extends ClassBuilder<Class<?>, String, IsolatedMethodBuilder> { 115 116 private static final Class<?> THIS_CLASS = new Object() { }.getClass(); 117 118 public IsolatedMethodBuilder(String clazz, Lookup lookup) { 119 super(ENABLE_POOL_PATCHES ? 120 new IsolatedMethodPatchingPoolHelper(clazz) : 121 new IsolatedMethodPoolHelper(clazz), 122 new IsolatedMethodTypeHelper(lookup)); 123 withThisClass(THIS_CLASS); 124 } 125 126 public Class<?> thisClass() { 127 return THIS_CLASS; 128 } 129 130 Object[] patches() { 131 return ((IsolatedMethodPoolHelper)poolHelper).patches(); 132 } 133 134 static String classToInternalName(Class<?> c) { 135 return c.getName().replace('.', '/'); 136 } 137 138 static class IsolatedMethodTypeHelper implements TypeHelper<Class<?>, String> { 139 140 BasicTypeHelper basicTypeHelper = new BasicTypeHelper(); 141 Lookup lookup; 142 143 IsolatedMethodTypeHelper(Lookup lookup) { 144 this.lookup = lookup; 145 } 146 147 @Override 148 public String elemtype(String s) { 149 return basicTypeHelper.elemtype(s); 150 } 151 152 @Override 153 public String arrayOf(String s) { 154 return basicTypeHelper.arrayOf(s); 155 } 156 157 @Override 158 public Iterator<String> parameterTypes(String s) { 159 return basicTypeHelper.parameterTypes(s); 160 } 161 162 @Override 163 public String fromTag(TypeTag tag) { 164 return basicTypeHelper.fromTag(tag); 165 } 166 167 @Override 168 public String returnType(String s) { 169 return basicTypeHelper.returnType(s); 170 } 171 172 @Override 173 public String type(Class<?> aClass) { 174 if (aClass.isArray()) { 175 return classToInternalName(aClass); 176 } else { 177 return (aClass.isInlineClass() ? "Q" : "L") + classToInternalName(aClass) + ";"; 178 } 179 } 180 181 @Override 182 public boolean isValue(String desc) { 183 Class<?> aClass = symbol(desc); 184 return aClass != null && aClass.isInlineClass(); 185 } 186 187 @Override 188 public Class<?> symbol(String desc) { 189 try { 190 if (desc.startsWith("[")) { 191 return Class.forName(desc.replaceAll("/", "."), true, lookup.lookupClass().getClassLoader()); 192 } else { 193 return Class.forName(basicTypeHelper.symbol(desc).replaceAll("/", "."), true, lookup.lookupClass().getClassLoader()); 194 } 195 } catch (ReflectiveOperationException ex) { 196 throw new AssertionError(ex); 197 } 198 } 199 200 @Override 201 public TypeTag tag(String s) { 202 return basicTypeHelper.tag(s); 203 } 204 205 @Override 206 public Class<?> symbolFrom(String s) { 207 return symbol(s); 208 } 209 210 @Override 211 public String commonSupertype(String t1, String t2) { 212 return basicTypeHelper.commonSupertype(t1, t2); 213 } 214 215 @Override 216 public String nullType() { 217 return basicTypeHelper.nullType(); 218 } 219 } 220 221 static class IsolatedMethodPoolHelper extends BytePoolHelper<Class<?>, String> { 222 final String clazz; 223 224 IsolatedMethodPoolHelper(String clazz) { 225 super(c -> from(c, clazz), s->s); 226 this.clazz = clazz; 227 } 228 229 Object[] patches() { 230 return null; 231 } 232 233 static String from(Class<?> c, String clazz) { 234 return c == THIS_CLASS ? clazz.replace('.', '/') 235 : classToInternalName(c); 236 } 237 } 238 239 @Override 240 public byte[] build() { 241 return super.build(); 242 } 243 } 244 245 static class IsolatedMethodPatchingPoolHelper extends IsolatedMethodPoolHelper { 246 247 public IsolatedMethodPatchingPoolHelper(String clazz) { 248 super(clazz); 249 } 250 251 Map<Object, CpPatch> cpPatches = new HashMap<>(); 252 int cph = 0; // for counting constant placeholders 253 254 static class CpPatch { 255 256 final int index; 257 final String placeholder; 258 final Object value; 259 260 CpPatch(int index, String placeholder, Object value) { 261 this.index = index; 262 this.placeholder = placeholder; 263 this.value = value; 264 } 265 266 public String toString() { 267 return "CpPatch/index="+index+",placeholder="+placeholder+",value="+value; 268 } 269 } 270 271 @Override 272 public int putValue(Object v) { 273 if (v instanceof String || v instanceof Integer || v instanceof Float || v instanceof Double || v instanceof Long) { 274 return super.putValue(v); 275 } 276 assert (!v.getClass().isPrimitive()) : v; 277 return patchPoolEntry(v); // CP patching support 278 } 279 280 int patchPoolEntry(Object v) { 281 String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + cph++; 282 if (cpPatches.containsKey(cpPlaceholder)) { 283 throw new InternalError("observed CP placeholder twice: " + cpPlaceholder); 284 } 285 // insert placeholder in CP and remember the patch 286 int index = super.putValue(cpPlaceholder); // TODO check if already in the constant pool 287 cpPatches.put(cpPlaceholder, new CpPatch(index, cpPlaceholder, v)); 288 return index; 289 } 290 291 @Override 292 Object[] patches() { 293 int size = size(); 294 Object[] res = new Object[size]; 295 for (CpPatch p : cpPatches.values()) { 296 if (p.index >= size) 297 throw new InternalError("bad cp patch index"); 298 res[p.index] = p.value; 299 } 300 return res; 301 } 302 303 private static String debugString(Object arg) { 304 // @@@ Cannot crack open a MH like with InvokerByteCodeGenerator.debugString 305 return arg.toString(); 306 } 307 } 308 309 public static class MethodHandleCodeBuilder<T extends MethodHandleCodeBuilder<T>> extends TypedCodeBuilder<Class<?>, String, byte[], T> { 310 311 BasicTypeHelper basicTypeHelper = new BasicTypeHelper(); 312 313 public MethodHandleCodeBuilder(jdk.experimental.bytecode.MethodBuilder<Class<?>, String, byte[]> methodBuilder) { 314 super(methodBuilder); 315 } 316 317 TypeTag getTagType(String s) { 318 return basicTypeHelper.tag(s); 319 } 320 321 public T ifcmp(String s, CondKind cond, CharSequence label) { 322 return super.ifcmp(getTagType(s), cond, label); 323 } 324 325 public T return_(String s) { 326 return super.return_(getTagType(s)); 327 } 328 } 329 330 static void maybeDump(final String className, final byte[] classFile) { 331 if (DUMP_CLASS_FILES_DIR != null) { 332 java.security.AccessController.doPrivileged( 333 new java.security.PrivilegedAction<>() { 334 public Void run() { 335 String dumpName = className.replace('.','/'); 336 Path dumpFile = Paths.get(DUMP_CLASS_FILES_DIR, dumpName + ".class"); 337 try { 338 Files.createDirectories(dumpFile.getParent()); 339 } catch (IOException e) { 340 throw new UncheckedIOException(e); 341 } 342 343 System.out.println("dump: " + dumpFile); 344 try (OutputStream os = Files.newOutputStream(dumpFile)) { 345 os.write(classFile); 346 } catch (IOException ex) { 347 throw new UncheckedIOException(ex); 348 } 349 return null; 350 } 351 }); 352 } 353 } 354 }