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