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