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 import valhalla.shady.ValueTypeHolder; 43 44 import java.lang.invoke.MethodHandle; 45 import java.lang.invoke.MethodHandles.Lookup; 46 import java.lang.invoke.MethodType; 47 import java.util.*; 48 import java.util.function.Consumer; 49 import java.util.function.Function; 50 51 /** 52 * Utility class for building method handles. 53 */ 54 public class MethodHandleBuilder { 55 56 static final Unsafe UNSAFE = Unsafe.getUnsafe(); 57 58 public static final boolean ENABLE_POOL_PATCHES; 59 60 static { 61 Properties props = GetPropertyAction.privilegedGetProperties(); 62 ENABLE_POOL_PATCHES = Boolean.parseBoolean( 63 props.getProperty("valhalla.enablePoolPatches")); 64 } 65 66 public static MethodHandle loadCode(Lookup lookup, String name, MethodType type, Consumer<? super MethodHandleCodeBuilder<?>> builder) { 67 return loadCode(lookup, name, name, type, builder); 68 } 69 70 public static MethodHandle loadCode(Lookup lookup, String className, String methodName, MethodType type, Consumer<? super MethodHandleCodeBuilder<?>> builder) { 71 String descriptor = type.toMethodDescriptorString(); 72 return loadCode(lookup, className, methodName, descriptor, MethodHandleCodeBuilder::new, 73 clazz -> { 74 try { 75 return lookup.findStatic(clazz, methodName, MethodType.fromMethodDescriptorString(descriptor, lookup.lookupClass().getClassLoader())); 76 } catch (ReflectiveOperationException ex) { 77 throw new IllegalStateException(ex); 78 } 79 }, 80 builder); 81 } 82 83 protected static <Z, C extends CodeBuilder<Class<?>, String, byte[], ?>> Z loadCode( 84 Lookup lookup, String className, String methodName, String type, 85 Function<MethodBuilder<Class<?>, String, byte[]>, ? extends C> builderFunc, 86 Function<Class<?>, Z> resFunc, Consumer<? super C> builder) { 87 88 IsolatedMethodBuilder isolatedMethodBuilder = new IsolatedMethodBuilder(className, lookup); 89 isolatedMethodBuilder 90 .withSuperclass(Object.class) 91 .withMajorVersion(52) 92 .withMinorVersion(0) 93 .withFlags(Flag.ACC_PUBLIC) 94 .withMethod(methodName, type, M -> 95 M.withFlags(Flag.ACC_STATIC, Flag.ACC_PUBLIC) 96 .withCode(builderFunc, builder)); 97 98 try { 99 byte[] barr = isolatedMethodBuilder.build(); 100 MinimalValueTypes_1_0.maybeDump(className, barr.clone()); 101 Class<?> clazz = UNSAFE.defineAnonymousClass(lookup.lookupClass(), barr, isolatedMethodBuilder.patches()); 102 UNSAFE.ensureClassInitialized(clazz); 103 return resFunc.apply(clazz); 104 } catch (Throwable e) { 105 throw new IllegalStateException(e); 106 } 107 } 108 109 public static class IsolatedMethodBuilder extends ClassBuilder<Class<?>, String, IsolatedMethodBuilder> { 110 111 private static final Class<?> THIS_CLASS = new Object() { }.getClass(); 112 113 public IsolatedMethodBuilder(String clazz, Lookup lookup) { 114 super(ENABLE_POOL_PATCHES ? 115 new IsolatedMethodPatchingPoolHelper(clazz) : 116 new IsolatedMethodPoolHelper(clazz), 117 new IsolatedMethodTypeHelper(lookup)); 118 withThisClass(THIS_CLASS); 119 } 120 121 public Class<?> thisClass() { 122 return 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 if (desc.endsWith("$Value;")) { 177 //value class - avoid Class.forName on the DVT 178 try { 179 int numDims = 0; 180 while (desc.startsWith("[")) { 181 desc = desc.substring(1); 182 numDims++; 183 } 184 Class<?> box = Class.forName(basicTypeHelper.symbol(desc) 185 .replaceAll("/", ".") 186 .replaceAll("\\$Value", ""), true, lookup.lookupClass().getClassLoader()); 187 ValueTypeHolder<?> vt = MinimalValueTypes_1_0.getValueFor(box); 188 return numDims == 0 ? 189 vt.valueClass() : vt.arrayValueClass(numDims); 190 } catch (ReflectiveOperationException ex) { 191 throw new AssertionError(ex); 192 } 193 } else { 194 try { 195 if (desc.startsWith("[")) { 196 return Class.forName(desc.replaceAll("/", "."), true, lookup.lookupClass().getClassLoader()); 197 } else { 198 return Class.forName(basicTypeHelper.symbol(desc).replaceAll("/", "."), true, lookup.lookupClass().getClassLoader()); 199 } 200 } catch (ReflectiveOperationException ex) { 201 throw new AssertionError(ex); 202 } 203 } 204 } 205 206 @Override 207 public TypeTag tag(String s) { 208 return basicTypeHelper.tag(s); 209 } 210 211 @Override 212 public Class<?> symbolFrom(String s) { 213 return symbol(s); 214 } 215 216 @Override 217 public String commonSupertype(String t1, String t2) { 218 return basicTypeHelper.commonSupertype(t1, t2); 219 } 220 221 @Override 222 public String nullType() { 223 return basicTypeHelper.nullType(); 224 } 225 } 226 227 static class IsolatedMethodPoolHelper implements PoolHelper<Class<?>, String, byte[]> { 228 BasicPoolHelper basicPoolHelper = new BasicPoolHelper(); 229 String clazz; 230 231 IsolatedMethodPoolHelper(String clazz) { 232 this.clazz = clazz; 233 } 234 235 String from(Class<?> c) { 236 String name; 237 boolean isValue = MinimalValueTypes_1_0.isValueType(c); 238 if (c == THIS_CLASS) { 239 //THIS_CLASS cannot be a DVT (by construction) - never mangle 240 name = clazz; 241 } else { 242 name = isValue ? 243 MinimalValueTypes_1_0.mangleValueClassName(c.getName()) : 244 c.getName(); 245 } 246 return name.replaceAll("\\.", "/"); 247 } 248 249 @Override 250 public int putClass(Class<?> symbol) { 251 return basicPoolHelper.putClass(from(symbol)); 252 } 253 254 @Override 255 public int putFieldRef(Class<?> owner, CharSequence name, String type) { 256 return basicPoolHelper.putFieldRef(from(owner), name, type); 257 } 258 259 @Override 260 public int putMethodRef(Class<?> owner, CharSequence name, String type, boolean isInterface) { 261 return basicPoolHelper.putMethodRef(from(owner), name, type, isInterface); 262 } 263 264 @Override 265 public int putUtf8(CharSequence s) { 266 return basicPoolHelper.putUtf8(s); 267 } 268 269 @Override 270 public int putType(String s) { 271 return basicPoolHelper.putType(s); 272 } 273 274 @Override 275 public int putValue(Object v) { 276 return basicPoolHelper.putValue(v); 277 } 278 279 @Override 280 public int putMethodType(String s) { 281 return basicPoolHelper.putMethodType(s); 282 } 283 284 @Override 285 public int putHandle(int refKind, Class<?> owner, CharSequence name, String type) { 286 return basicPoolHelper.putHandle(refKind, from(owner), name, type); 287 } 288 289 @Override 290 public int putInvokeDynamic(CharSequence invokedName, String invokedType, int bskKind, Class<?> bsmClass, CharSequence bsmName, String bsmType, Object... staticArgs) { 291 return basicPoolHelper.putInvokeDynamic(invokedName, invokedType, bskKind, from(bsmClass), bsmName, bsmType, staticArgs); 292 } 293 294 @Override 295 public int size() { 296 return basicPoolHelper.size(); 297 } 298 299 @Override 300 public byte[] entries() { 301 return basicPoolHelper.entries(); 302 } 303 304 Object[] patches() { 305 return null; 306 } 307 } 308 309 @Override 310 public byte[] build() { 311 return super.build(); 312 } 313 } 314 315 static class IsolatedMethodPatchingPoolHelper extends IsolatedMethodPoolHelper { 316 317 public IsolatedMethodPatchingPoolHelper(String clazz) { 318 super(clazz); 319 } 320 321 Map<Object, CpPatch> cpPatches = new HashMap<>(); 322 int cph = 0; // for counting constant placeholders 323 324 static class CpPatch { 325 326 final int index; 327 final String placeholder; 328 final Object value; 329 330 CpPatch(int index, String placeholder, Object value) { 331 this.index = index; 332 this.placeholder = placeholder; 333 this.value = value; 334 } 335 336 public String toString() { 337 return "CpPatch/index="+index+",placeholder="+placeholder+",value="+value; 338 } 339 } 340 341 @Override 342 public int putValue(Object v) { 343 if (v instanceof String || v instanceof Integer || v instanceof Float || v instanceof Double || v instanceof Long) { 344 return basicPoolHelper.putValue(v); 345 } 346 assert (!v.getClass().isPrimitive()) : v; 347 return patchPoolEntry(v); // CP patching support 348 } 349 350 int patchPoolEntry(Object v) { 351 String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + cph++; 352 if (MinimalValueTypes_1_0.DUMP_CLASS_FILES) cpPlaceholder += " <<" + debugString(v) + ">>"; 353 if (cpPatches.containsKey(cpPlaceholder)) { 354 throw new InternalError("observed CP placeholder twice: " + cpPlaceholder); 355 } 356 // insert placeholder in CP and remember the patch 357 int index = basicPoolHelper.putValue(cpPlaceholder); // TODO check if already in the constant pool 358 cpPatches.put(cpPlaceholder, new CpPatch(index, cpPlaceholder, v)); 359 return index; 360 } 361 362 @Override 363 Object[] patches() { 364 int size = size(); 365 Object[] res = new Object[size]; 366 for (CpPatch p : cpPatches.values()) { 367 if (p.index >= size) 368 throw new InternalError("bad cp patch index"); 369 res[p.index] = p.value; 370 } 371 return res; 372 } 373 374 private static String debugString(Object arg) { 375 // @@@ Cannot crack open a MH like with InvokerByteCodeGenerator.debugString 376 return arg.toString(); 377 } 378 } 379 380 public static class MethodHandleCodeBuilder<T extends MethodHandleCodeBuilder<T>> extends TypedCodeBuilder<Class<?>, String, byte[], T> { 381 382 BasicTypeHelper basicTypeHelper = new BasicTypeHelper(); 383 384 public MethodHandleCodeBuilder(jdk.experimental.bytecode.MethodBuilder<Class<?>, String, byte[]> methodBuilder) { 385 super(methodBuilder); 386 } 387 388 TypeTag getTagType(String s) { 389 return basicTypeHelper.tag(s); 390 } 391 392 public T ifcmp(String s, CondKind cond, CharSequence label) { 393 return super.ifcmp(getTagType(s), cond, label); 394 } 395 396 public T return_(String s) { 397 return super.return_(getTagType(s)); 398 } 399 } 400 }