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