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