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 }