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 Class<?> || v instanceof MethodHandle || v instanceof MethodType) {
 322                 return patchPoolEntry(v);
 323             }
 324             return basicPoolHelper.putValue(v);
 325         }
 326 
 327         int patchPoolEntry(Object v) {
 328             String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + cph++;
 329             if (MinimalValueTypes_1_0.DUMP_CLASS_FILES) cpPlaceholder += " <<" + debugString(v) + ">>";
 330             if (cpPatches.containsKey(cpPlaceholder)) {
 331                 throw new InternalError("observed CP placeholder twice: " + cpPlaceholder);
 332             }
 333             // insert placeholder in CP and remember the patch
 334             int index = basicPoolHelper.putValue(cpPlaceholder);  // TODO check if already in the constant pool
 335             cpPatches.put(cpPlaceholder, new CpPatch(index, cpPlaceholder, v));
 336             return index;
 337         }
 338 
 339         @Override
 340         Object[] patches() {
 341             int size = size();
 342             Object[] res = new Object[size];
 343             for (CpPatch p : cpPatches.values()) {
 344                 if (p.index >= size)
 345                     throw new InternalError("bad cp patch index");
 346                 res[p.index] = p.value;
 347             }
 348             return res;
 349         }
 350 
 351         private static String debugString(Object arg) {
 352             // @@@ Cannot crack open a MH like with InvokerByteCodeGenerator.debugString
 353             return arg.toString();
 354         }
 355     }
 356 
 357     public static class MethodHandleCodeBuilder extends TypedCodeBuilder<Class<?>, String, byte[], MethodHandleCodeBuilder> {
 358 
 359         BasicTypeHelper basicTypeHelper = new BasicTypeHelper();
 360 
 361         public MethodHandleCodeBuilder(jdk.experimental.bytecode.MethodBuilder<Class<?>, String, byte[]> methodBuilder) {
 362             super(methodBuilder);
 363         }
 364 
 365         TypeTag getTagType(String s) {
 366             return basicTypeHelper.tag(s);
 367         }
 368 
 369         public MethodHandleCodeBuilder ifcmp(String s, CondKind cond, CharSequence label) {
 370             return super.ifcmp(getTagType(s), cond, label);
 371         }
 372 
 373         public MethodHandleCodeBuilder return_(String s) {
 374             return super.return_(getTagType(s));
 375         }
 376     }
 377 }