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