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.GetBooleanAction;
  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.security.AccessController;
  47 import java.util.HashMap;
  48 import java.util.Iterator;
  49 import java.util.Map;
  50 import java.util.PropertyPermission;
  51 import java.util.function.Consumer;
  52 import java.util.function.Function;
  53 
  54 /**
  55  * Utility class for building method handles.
  56  */
  57 public class MethodHandleBuilder {
  58 
  59     static final Unsafe UNSAFE = Unsafe.getUnsafe();
  60 
  61     static final boolean ENABLE_POOL_PATCHES;

  62 
  63     static {
  64         final String key = "valhalla.enablePoolPatches";
  65         ENABLE_POOL_PATCHES = key == null ? false :
  66                 AccessController.doPrivileged(
  67                 new GetBooleanAction(key), null,
  68                 new PropertyPermission(key , "read"));
  69     }
  70 
  71     public static MethodHandle loadCode(Lookup lookup, String name, MethodType type, Consumer<? super MethodHandleCodeBuilder> builder) {
  72         return loadCode(lookup, name, type.toMethodDescriptorString(), builder);
  73     }
  74 
  75     public static MethodHandle loadCode(Lookup lookup, String name, String type, Consumer<? super MethodHandleCodeBuilder> builder) {
  76         return loadCode(lookup, name, name, type, MethodHandleCodeBuilder::new,
  77                     clazz -> {
  78                         try {
  79                             return lookup.findStatic(clazz, name, MethodType.fromMethodDescriptorString(type, lookup.lookupClass().getClassLoader()));
  80                         } catch (ReflectiveOperationException ex) {
  81                             throw new IllegalStateException(ex);
  82                         }
  83                     },
  84                     builder);
  85     }
  86 
  87     protected static <Z, C extends CodeBuilder<Class<?>, String, byte[], ?>> Z loadCode(
  88             Lookup lookup, String className, String methodName, String type,
  89             Function<MethodBuilder<Class<?>, String, byte[]>, ? extends C> builderFunc,
  90             Function<Class<?>, Z> resFunc, Consumer<? super C> builder) {
  91 
  92         IsolatedMethodBuilder isolatedMethodBuilder = new IsolatedMethodBuilder(className, lookup);
  93         isolatedMethodBuilder
  94                 .withSuperclass(Object.class)
  95                 .withMajorVersion(52)
  96                 .withMinorVersion(0)
  97                 .withFlags(Flag.ACC_PUBLIC)
  98                 .withMethod(methodName, type, M ->
  99                         M.withFlags(Flag.ACC_STATIC, Flag.ACC_PUBLIC)
 100                                 .withCode(builderFunc, builder));
 101 
 102         try {
 103             byte[] barr = isolatedMethodBuilder.build();
 104             MinimalValueTypes_1_0.maybeDump(className, barr.clone());
 105             Class<?> clazz = UNSAFE.defineAnonymousClass(lookup.lookupClass(), barr, isolatedMethodBuilder.patches());
 106             UNSAFE.ensureClassInitialized(clazz);
 107             return resFunc.apply(clazz);
 108         } catch (Throwable e) {
 109              throw new IllegalStateException(e);
 110         }
 111     }
 112 
 113     static class IsolatedMethodBuilder extends ClassBuilder<Class<?>, String, IsolatedMethodBuilder> {
 114 
 115         private static final Class<?> THIS_CLASS = new Object() { }.getClass();
 116 
 117         IsolatedMethodBuilder(String clazz, Lookup lookup) {
 118             super(ENABLE_POOL_PATCHES ?
 119                             new IsolatedMethodPatchingPoolHelper(clazz) :
 120                             new IsolatedMethodPoolHelper(clazz),
 121                   new IsolatedMethodTypeHelper(lookup));
 122             withThisClass(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                 try {
 177                     if (desc.startsWith("[")) {
 178                        return Class.forName(desc.replaceAll("/", "."), true, lookup.lookupClass().getClassLoader());
 179                     } else {
 180                         return Class.forName(basicTypeHelper.symbol(desc).replaceAll("/", "."), true, lookup.lookupClass().getClassLoader());
 181                     }
 182                 } catch (ReflectiveOperationException ex) {
 183                     throw new AssertionError(ex);
 184                 }
 185             }
 186 
 187             @Override
 188             public TypeTag tag(String s) {
 189                 return basicTypeHelper.tag(s);
 190             }
 191 
 192             @Override
 193             public Class<?> symbolFrom(String s) {
 194                 return symbol(s);
 195             }
 196 
 197             @Override
 198             public String commonSupertype(String t1, String t2) {
 199                 return basicTypeHelper.commonSupertype(t1, t2);
 200             }
 201 
 202             @Override
 203             public String nullType() {
 204                 return basicTypeHelper.nullType();
 205             }
 206         }
 207 
 208         static class IsolatedMethodPoolHelper implements PoolHelper<Class<?>, String, byte[]> {
 209             BasicPoolHelper basicPoolHelper = new BasicPoolHelper();
 210             String clazz;
 211 
 212             IsolatedMethodPoolHelper(String clazz) {
 213                 this.clazz = clazz;
 214             }
 215 
 216             String from(Class<?> c) {
 217                 String name = c == THIS_CLASS ? clazz : c.getName();









 218                 return name.replaceAll("\\.", "/");
 219             }
 220 
 221             @Override
 222             public int putClass(Class<?> symbol) {
 223                 return basicPoolHelper.putClass(from(symbol));
 224             }
 225 
 226             @Override
 227             public int putFieldRef(Class<?> owner, CharSequence name, String type) {
 228                 return basicPoolHelper.putFieldRef(from(owner), name, type);
 229             }
 230 
 231             @Override
 232             public int putMethodRef(Class<?> owner, CharSequence name, String type, boolean isInterface) {
 233                 return basicPoolHelper.putMethodRef(from(owner), name, type, isInterface);
 234             }
 235 
 236             @Override
 237             public int putUtf8(CharSequence s) {
 238                 return basicPoolHelper.putUtf8(s);
 239             }
 240 
 241             @Override
 242             public int putType(String s) {
 243                 return basicPoolHelper.putType(s);
 244             }
 245 
 246             @Override
 247             public int putValue(Object v) {
 248                 return basicPoolHelper.putValue(v);
 249             }
 250 
 251             @Override
 252             public int putMethodType(String s) {
 253                 return basicPoolHelper.putMethodType(s);
 254             }
 255 
 256             @Override
 257             public int putHandle(int refKind, Class<?> owner, CharSequence name, String type) {
 258                 return basicPoolHelper.putHandle(refKind, from(owner), name, type);
 259             }
 260 
 261             @Override
 262             public int putInvokeDynamic(CharSequence invokedName, String invokedType, int bskKind, Class<?> bsmClass, CharSequence bsmName, String bsmType, Object... staticArgs) {
 263                 return basicPoolHelper.putInvokeDynamic(invokedName, invokedType, bskKind, from(bsmClass), bsmName, bsmType, staticArgs);
 264             }
 265 
 266             @Override
 267             public int size() {
 268                 return basicPoolHelper.size();
 269             }
 270 
 271             @Override
 272             public byte[] entries() {
 273                 return basicPoolHelper.entries();
 274             }
 275 
 276             Object[] patches() {
 277                 return null;
 278             }
 279         }
 280 
 281         @Override
 282         public byte[] build() {
 283             return super.build();
 284         }
 285     }
 286 
 287     static class IsolatedMethodPatchingPoolHelper extends IsolatedMethodPoolHelper {
 288 
 289         public IsolatedMethodPatchingPoolHelper(String clazz) {
 290             super(clazz);
 291         }
 292 
 293         Map<Object, CpPatch> cpPatches = new HashMap<>();
 294         int cph = 0;  // for counting constant placeholders
 295 
 296         static class CpPatch {
 297 
 298             final int index;
 299             final String placeholder;
 300             final Object value;
 301 
 302             CpPatch(int index, String placeholder, Object value) {
 303                 this.index = index;
 304                 this.placeholder = placeholder;
 305                 this.value = value;
 306             }
 307 
 308             public String toString() {
 309                 return "CpPatch/index="+index+",placeholder="+placeholder+",value="+value;
 310             }
 311         }
 312 
 313         @Override
 314         public int putValue(Object v) {
 315             if (v instanceof Class<?> || v instanceof MethodHandle || v instanceof MethodType) {
 316                 return patchPoolEntry(v);
 317             }
 318             return basicPoolHelper.putValue(v);
 319         }
 320 
 321         int patchPoolEntry(Object v) {
 322             String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + cph++;
 323             if (MinimalValueTypes_1_0.DUMP_CLASS_FILES) cpPlaceholder += " <<" + debugString(v) + ">>";
 324             if (cpPatches.containsKey(cpPlaceholder)) {
 325                 throw new InternalError("observed CP placeholder twice: " + cpPlaceholder);
 326             }
 327             // insert placeholder in CP and remember the patch
 328             int index = basicPoolHelper.putValue(cpPlaceholder);  // TODO check if already in the constant pool
 329             cpPatches.put(cpPlaceholder, new CpPatch(index, cpPlaceholder, v));
 330             return index;
 331         }
 332 
 333         @Override
 334         Object[] patches() {
 335             int size = size();
 336             Object[] res = new Object[size];
 337             for (CpPatch p : cpPatches.values()) {
 338                 if (p.index >= size)
 339                     throw new InternalError("bad cp patch index");
 340                 res[p.index] = p.value;
 341             }
 342             return res;
 343         }
 344 
 345         private static String debugString(Object arg) {
 346             // @@@ Cannot crack open a MH like with InvokerByteCodeGenerator.debugString
 347             return arg.toString();
 348         }
 349     }
 350 
 351     public static class MethodHandleCodeBuilder extends TypedCodeBuilder<Class<?>, String, byte[], MethodHandleCodeBuilder> {
 352 
 353         BasicTypeHelper basicTypeHelper = new BasicTypeHelper();
 354 
 355         public MethodHandleCodeBuilder(jdk.experimental.bytecode.MethodBuilder<Class<?>, String, byte[]> methodBuilder) {
 356             super(methodBuilder);
 357         }
 358 
 359         TypeTag getTagType(String s) {
 360             return basicTypeHelper.tag(s);
 361         }
 362 
 363         public MethodHandleCodeBuilder ifcmp(String s, CondKind cond, CharSequence label) {
 364             return super.ifcmp(getTagType(s), cond, label);
 365         }
 366 
 367         public MethodHandleCodeBuilder return_(String s) {
 368             return super.return_(getTagType(s));
 369         }
 370     }
 371 }
--- EOF ---