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