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 }