1 /*
   2  * Copyright 2008-2009 Sun Microsystems, Inc.  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.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any questions.
  24  */
  25 
  26 package impl.java.dyn.util;
  27 
  28 import impl.java.dyn.Access;
  29 import impl.java.dyn.AdapterMethodHandle;
  30 import java.dyn.AnonymousClassLoader;
  31 import java.dyn.ConstantPoolParser;
  32 import java.dyn.ConstantPoolPatch;
  33 import java.dyn.ConstantPoolVisitor;
  34 import java.dyn.InvalidConstantPoolFormatException;
  35 import java.dyn.MethodHandle;
  36 import java.dyn.MethodHandles;
  37 import java.dyn.MethodType;
  38 import java.dyn.WrongMethodTypeException;
  39 import java.io.IOException;
  40 import java.lang.reflect.Constructor;
  41 import java.lang.reflect.InvocationTargetException;
  42 import java.util.IdentityHashMap;
  43 
  44 /**
  45  * Emulation of method handle invocation.
  46  * Not needed if javac supports direct invocation of MethodHandle.invoke.
  47  * @author jrose
  48  */
  49 public abstract
  50 class MethodHandleInvoker implements Cloneable {
  51     private static final Access IMPL_TOKEN = Access.getToken();
  52 
  53 
  54     /** Exact type for all handles targeted by this invoker. */
  55     protected final MethodType exactType;
  56 
  57     /** Condensed information about the return type, one of "VLIJFDZBSC". */
  58     protected final char rtypec;
  59 
  60     /** Adapter which converts the approximate type to the exact exactType. */
  61     protected final MethodHandle adapter;
  62 
  63     /** Maximum number of arguments allowed.
  64      *  This is an implementation limit.
  65      */
  66     public static final int ARGUMENT_MAX;
  67     /** Maximum number of double or long arguments allowed.
  68      *  This is an implementation limit.
  69      */
  70     public static final int LONG_ARGUMENT_MAX;
  71     static {
  72         LONG_ARGUMENT_MAX = 3;  // %%% depends on stack headroom
  73     }
  74 
  75     public MethodType type() { return exactType; }
  76 
  77     protected MethodHandleInvoker(MethodType exactType, MethodHandle adapter) {
  78         this.exactType = exactType;
  79         this.rtypec = Wrappers.basicTypeChar(exactType.returnType());
  80         this.adapter = adapter;
  81     }
  82 
  83     static MethodHandle makeAdapter(MethodType exactType, MethodType approxType) {
  84         // For each argument, convert incoming Object to the exact type needed.
  85         int len = exactType.parameterCount();
  86         assert(len == approxType.parameterCount());
  87         if (exactType.parameterSlotCount() > len + LONG_ARGUMENT_MAX)
  88             throw new IllegalArgumentException("too many long arguments in "+exactType);
  89         MethodHandle invoker = MethodHandles.findVirtual(MethodHandle.class, "invoke", exactType);
  90         MethodType adapterType = approxType.insertParameterType(0, MethodHandle.class);
  91         return AdapterMethodHandle.makePairwiseConversion(IMPL_TOKEN, adapterType, invoker);
  92     }
  93 
  94     public Object invoke_0(MethodHandle mh)
  95         {   throw wrongType(mh);  }
  96     public Object invoke_1(MethodHandle mh, Object a0)
  97         {   throw wrongType(mh);  }
  98     public Object invoke_2(MethodHandle mh, Object a0, Object a1)
  99         {   throw wrongType(mh);  }
 100     public Object invoke_3(MethodHandle mh, Object a0, Object a1, Object a2)
 101         {   throw wrongType(mh);  }
 102     public Object invoke_4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3)
 103         {   throw wrongType(mh);  }
 104     public Object invoke_5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4)
 105         {   throw wrongType(mh);  }
 106     
 107     /** Reflective style generic invocation.  This always delegates
 108      *  to one of the invoke_X methods.
 109      * @param mh method handle to invoke (must be of exactly correct type)
 110      * @param args array of arguments to send to method (maybe null if empty)
 111      * @return
 112      */
 113     final // try this...
 114     public Object invoke(MethodHandle mh, Object ... args) {
 115         switch (args == null ? 0 : args.length) {
 116         case 0: return invoke_0(mh);
 117         case 1: return invoke_1(mh, args[0]);
 118         case 2: return invoke_2(mh, args[0], args[1]);
 119         case 3: return invoke_3(mh, args[0], args[1], args[2]);
 120         case 4: return invoke_4(mh, args[0], args[1], args[2], args[3]);
 121         case 5: return invoke_5(mh, args[0], args[1], args[2], args[3], args[4]);
 122         }
 123         throw wrongType(mh);
 124     }
 125 
 126     // This class is not used after compile time.
 127     // It is renamed away to MethodHandle itself, to call the MHI.adapter.
 128     // TO DO: Update javac so we can call directly to polymorphic MH.invoke.
 129     private static abstract class FakeMethodHandle extends MethodHandle {
 130         public FakeMethodHandle() { super(null, null); }
 131         // here are all the invokes we need to link against:
 132         public abstract void   fake_invoke_V0(MethodHandle mh);
 133         public abstract Object fake_invoke_L0(MethodHandle mh);
 134         public abstract int    fake_invoke_I0(MethodHandle mh);
 135         public abstract long   fake_invoke_J0(MethodHandle mh);
 136         public abstract double fake_invoke_F0(MethodHandle mh);
 137         public abstract double fake_invoke_D0(MethodHandle mh);
 138         public abstract void   fake_invoke_V1(MethodHandle mh, Object a0);
 139         public abstract Object fake_invoke_L1(MethodHandle mh, Object a0);
 140         public abstract int    fake_invoke_I1(MethodHandle mh, Object a0);
 141         public abstract long   fake_invoke_J1(MethodHandle mh, Object a0);
 142         public abstract float  fake_invoke_F1(MethodHandle mh, Object a0);
 143         public abstract double fake_invoke_D1(MethodHandle mh, Object a0);
 144         public abstract void   fake_invoke_V2(MethodHandle mh, Object a0, Object a1);
 145         public abstract Object fake_invoke_L2(MethodHandle mh, Object a0, Object a1);
 146         public abstract int    fake_invoke_I2(MethodHandle mh, Object a0, Object a1);
 147         public abstract long   fake_invoke_J2(MethodHandle mh, Object a0, Object a1);
 148         public abstract float  fake_invoke_F2(MethodHandle mh, Object a0, Object a1);
 149         public abstract double fake_invoke_D2(MethodHandle mh, Object a0, Object a1);
 150         public abstract void   fake_invoke_V3(MethodHandle mh, Object a0, Object a1, Object a2);
 151         public abstract Object fake_invoke_L3(MethodHandle mh, Object a0, Object a1, Object a2);
 152         public abstract int    fake_invoke_I3(MethodHandle mh, Object a0, Object a1, Object a2);
 153         public abstract long   fake_invoke_J3(MethodHandle mh, Object a0, Object a1, Object a2);
 154         public abstract float  fake_invoke_F3(MethodHandle mh, Object a0, Object a1, Object a2);
 155         public abstract double fake_invoke_D3(MethodHandle mh, Object a0, Object a1, Object a2);
 156         public abstract void   fake_invoke_V4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3);
 157         public abstract Object fake_invoke_L4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3);
 158         public abstract int    fake_invoke_I4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3);
 159         public abstract long   fake_invoke_J4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3);
 160         public abstract float  fake_invoke_F4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3);
 161         public abstract double fake_invoke_D4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3);
 162         public abstract void   fake_invoke_V5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4);
 163         public abstract Object fake_invoke_L5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4);
 164         public abstract int    fake_invoke_I5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4);
 165         public abstract long   fake_invoke_J5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4);
 166         public abstract float  fake_invoke_F5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4);
 167         public abstract double fake_invoke_D5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4);
 168     }
 169     private static String FMHInvokeName(MethodType approxType) {
 170         assert(isFMHInvokeType(approxType)) : approxType;
 171         return "fake_invoke_"
 172                 + Wrappers.basicTypeChar(approxType.returnType())
 173                 + approxType.parameterCount();
 174     }
 175     private static boolean isFMHInvokeType(MethodType approxType) {
 176         Class<?> rtype = approxType.returnType();
 177         char rtc = Wrappers.basicTypeChar(approxType.returnType());
 178         if ("VIJFD".indexOf(rtc) < 0 && rtype != Object.class)
 179             return false;
 180         int len = approxType.parameterCount();
 181         for (int i = 0; i < len; i++)
 182             if (approxType.parameterType(i) != Object.class)
 183                 return false;
 184         return true;
 185     }
 186 
 187     protected Object wrap(int value) {
 188         switch (rtypec) {
 189         case 'Z': return (value != 0);
 190         case 'B': return (byte)value;
 191         case 'S': return (short)value;
 192         case 'C': return (char)value;
 193         }
 194         return value;
 195     }
 196 
 197     static class L0 extends MethodHandleInvoker {
 198         public L0(MethodType type, MethodHandle adapter) { super(type, adapter); }
 199         @Override public Object invoke_0(MethodHandle mh) {
 200             checkType(mh);
 201             switch (rtypec) {
 202             // Note: Could unswitch this into 5 classes, but too messy.
 203             case 'L': return      ((FakeMethodHandle)adapter).fake_invoke_L0(mh);
 204             default:  return wrap(((FakeMethodHandle)adapter).fake_invoke_I0(mh));
 205             case 'J': return      ((FakeMethodHandle)adapter).fake_invoke_J0(mh);
 206             case 'F': return      ((FakeMethodHandle)adapter).fake_invoke_F0(mh);
 207             case 'D': return      ((FakeMethodHandle)adapter).fake_invoke_D0(mh);
 208             case 'V':             ((FakeMethodHandle)adapter).fake_invoke_V0(mh);
 209             /* Here is the sort of code we will use when we get javac support:
 210             case 'L': return      adapter.invoke(mh);
 211             default:  return wrap(adapter.<int>invoke(mh));
 212             case 'J': return      adapter.<long>invoke(mh);
 213             case 'F': return      adapter.<float>invoke(mh);
 214             case 'D': return      adapter.<double>invoke(mh);
 215             case 'V':             adapter.<void>invoke(mh);
 216              */
 217             }
 218             return null;
 219         }
 220     }
 221 
 222     static class L1 extends MethodHandleInvoker {
 223         public L1(MethodType type, MethodHandle adapter) { super(type, adapter); }
 224         @Override public Object invoke_1(MethodHandle mh, Object a0) {
 225             checkType(mh);
 226             switch (rtypec) {
 227             case 'L': return      ((FakeMethodHandle)adapter).fake_invoke_L1(mh, a0);
 228             default:  return wrap(((FakeMethodHandle)adapter).fake_invoke_I1(mh, a0));
 229             case 'J': return      ((FakeMethodHandle)adapter).fake_invoke_J1(mh, a0);
 230             case 'F': return      ((FakeMethodHandle)adapter).fake_invoke_F1(mh, a0);
 231             case 'D': return      ((FakeMethodHandle)adapter).fake_invoke_D1(mh, a0);
 232             case 'V':             ((FakeMethodHandle)adapter).fake_invoke_V1(mh, a0);
 233             }
 234             return null;
 235         }
 236     }
 237 
 238     static class L2 extends MethodHandleInvoker {
 239         public L2(MethodType type, MethodHandle adapter) { super(type, adapter); }
 240         @Override public Object invoke_2(MethodHandle mh, Object a0, Object a1) {
 241             checkType(mh);
 242             switch (rtypec) {
 243             case 'L': return      ((FakeMethodHandle)adapter).fake_invoke_L2(mh, a0, a1);
 244             default:  return wrap(((FakeMethodHandle)adapter).fake_invoke_I2(mh, a0, a1));
 245             case 'J': return      ((FakeMethodHandle)adapter).fake_invoke_J2(mh, a0, a1);
 246             case 'F': return      ((FakeMethodHandle)adapter).fake_invoke_F2(mh, a0, a1);
 247             case 'D': return      ((FakeMethodHandle)adapter).fake_invoke_D2(mh, a0, a1);
 248             case 'V':             ((FakeMethodHandle)adapter).fake_invoke_V2(mh, a0, a1);
 249             }
 250             return null;
 251         }
 252     }
 253 
 254     static class L3 extends MethodHandleInvoker {
 255         public L3(MethodType type, MethodHandle adapter) { super(type, adapter); }
 256         @Override public Object invoke_3(MethodHandle mh, Object a0, Object a1, Object a2) {
 257             checkType(mh);
 258             switch (rtypec) {
 259             case 'L': return      ((FakeMethodHandle)adapter).fake_invoke_L3(mh, a0, a1, a2);
 260             default:  return wrap(((FakeMethodHandle)adapter).fake_invoke_I3(mh, a0, a1, a2));
 261             case 'J': return      ((FakeMethodHandle)adapter).fake_invoke_J3(mh, a0, a1, a2);
 262             case 'F': return      ((FakeMethodHandle)adapter).fake_invoke_F3(mh, a0, a1, a2);
 263             case 'D': return      ((FakeMethodHandle)adapter).fake_invoke_D3(mh, a0, a1, a2);
 264             case 'V':             ((FakeMethodHandle)adapter).fake_invoke_V3(mh, a0, a1, a2);
 265             }
 266             return null;
 267         }
 268     }
 269 
 270     static class L4 extends MethodHandleInvoker {
 271         public L4(MethodType type, MethodHandle adapter) { super(type, adapter); }
 272         @Override public Object invoke_4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3) {
 273             checkType(mh);
 274             switch (rtypec) {
 275             case 'L': return      ((FakeMethodHandle)adapter).fake_invoke_L4(mh, a0, a1, a2, a3);
 276             default:  return wrap(((FakeMethodHandle)adapter).fake_invoke_I4(mh, a0, a1, a2, a3));
 277             case 'J': return      ((FakeMethodHandle)adapter).fake_invoke_J4(mh, a0, a1, a2, a3);
 278             case 'F': return      ((FakeMethodHandle)adapter).fake_invoke_F4(mh, a0, a1, a2, a3);
 279             case 'D': return      ((FakeMethodHandle)adapter).fake_invoke_D4(mh, a0, a1, a2, a3);
 280             case 'V':             ((FakeMethodHandle)adapter).fake_invoke_V4(mh, a0, a1, a2, a3);
 281             }
 282             return null;
 283         }
 284     }
 285 
 286     static class L5 extends MethodHandleInvoker {
 287         public L5(MethodType type, MethodHandle adapter) { super(type, adapter); }
 288         @Override public Object invoke_5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4) {
 289             checkType(mh);
 290             switch (rtypec) {
 291             case 'L': return      ((FakeMethodHandle)adapter).fake_invoke_L5(mh, a0, a1, a2, a3, a4);
 292             default:  return wrap(((FakeMethodHandle)adapter).fake_invoke_I5(mh, a0, a1, a2, a3, a4));
 293             case 'J': return      ((FakeMethodHandle)adapter).fake_invoke_J5(mh, a0, a1, a2, a3, a4);
 294             case 'F': return      ((FakeMethodHandle)adapter).fake_invoke_F5(mh, a0, a1, a2, a3, a4);
 295             case 'D': return      ((FakeMethodHandle)adapter).fake_invoke_D5(mh, a0, a1, a2, a3, a4);
 296             case 'V':             ((FakeMethodHandle)adapter).fake_invoke_V5(mh, a0, a1, a2, a3, a4);
 297             }
 298             return null;
 299         }
 300     }
 301 
 302     private static Class<?>[] L_CLASSES
 303         = { L0.class, L1.class, L2.class, L3.class, L4.class, L5.class };
 304     static { ARGUMENT_MAX = L_CLASSES.length + 1; }
 305 
 306     public static MethodHandleInvoker make(MethodType type) {
 307         MethodHandleInvoker inv = null;
 308         synchronized (invokers) {
 309             inv = invokers.get(type);
 310         }
 311         if (inv != null)  return inv;
 312         inv = makeNew(type);
 313         synchronized (invokers) {
 314             MethodHandleInvoker inv2 = invokers.get(type);
 315             if (inv2 == null)
 316                 invokers.put(type, inv);
 317             else
 318                 inv = inv2;
 319         }
 320         System.out.println("new invoker: "+inv);
 321         return inv;
 322     }
 323 
 324     static MethodType approxType(MethodType exactType) {
 325         int len = exactType.parameterCount();
 326         if (len > ARGUMENT_MAX)
 327             throw new IllegalArgumentException("too many arguments for invoker: "+exactType);
 328         // The JVM can insert casts and unboxing for us in a native adapter.
 329         MethodType approxType = MethodType.makeGeneric(len);
 330         // But the return type must be exact, except for subword types.
 331         // Convert subwords to int, since the JVM an narrow them back down.
 332         Class<?> rtype = exactType.returnType();
 333         switch (Wrappers.basicTypeChar(rtype)) {
 334         case 'L':
 335             rtype = Object.class; break;
 336         case 'Z': case 'B': case 'S': case 'C':
 337             rtype = int.class; break;
 338         }
 339         approxType = approxType.changeReturnType(rtype);
 340         return approxType;
 341     }
 342 
 343     private static MethodHandleInvoker makeNew(MethodType exactType) {
 344         MethodHandleInvoker inv = null;
 345         Exception ex1 = null;
 346         MethodType approxType = approxType(exactType);
 347         MethodHandle adapter = makeAdapter(exactType, approxType);
 348         Class<? extends MethodHandleInvoker> template = null;
 349         Class<? extends MethodHandleInvoker> instance = null;
 350         Constructor<? extends MethodHandleInvoker> constr = null;
 351         template = L_CLASSES[approxType.parameterCount()].asSubclass(MethodHandleInvoker.class);
 352         {
 353             try {
 354                 instance = expandTemplate(template, approxType, exactType);
 355                 // When we get rid of the fakery, it will be just
 356                 // constr = template.getConstructor
 357                 constr = instance.getConstructor(MethodType.class, MethodHandle.class);
 358                 inv = constr.newInstance(exactType, adapter);
 359             } catch (IOException ex) {
 360                 ex1 = ex;
 361             } catch (InvalidConstantPoolFormatException ex) {
 362                 ex1 = ex;
 363             } catch (InstantiationException ex) {
 364                 ex1 = ex;
 365             } catch (IllegalAccessException ex) {
 366                 ex1 = ex;
 367             } catch (NoSuchMethodException ex) {
 368                 ex1 = ex;
 369             } catch (IllegalArgumentException ex) {
 370                 ex1 = ex;
 371             } catch (InvocationTargetException ex) {
 372                 ex1 = ex;
 373             }
 374         }
 375         if (inv == null) {
 376             printex(ex1);
 377             throw new InternalError();
 378         }
 379         return inv;
 380     }
 381     private static void printex(Exception ex) {
 382         System.out.println("*** Unexpected exception in "+MethodHandleInvoker.class);
 383         System.out.println(ex);
 384         ex.printStackTrace(System.out);
 385     }
 386 
 387     private static final AnonymousClassLoader LOADER
 388             = new AnonymousClassLoader(MethodHandleInvoker.class);
 389 
 390     private static String utf8Name(Class<?> cls) {
 391         return cls.getName().replace('.', '/');
 392     }
 393 
 394     private static class TemplateExpander extends ConstantPoolVisitor {
 395         ConstantPoolParser cp;
 396         ConstantPoolPatch patch;
 397 
 398         // Pairs of strings to be rewritten:
 399         String fakeMHName = utf8Name(FakeMethodHandle.class);
 400         String realMHName = utf8Name(MethodHandle.class);
 401         boolean didMHName;
 402 
 403         String fakeInvokeName, realInvokeName = "invoke";
 404         boolean didInvokeName;
 405 
 406         @Override
 407         public void visitUTF8(int index, byte tag, String utf8) {
 408             String orig = utf8;
 409             if (utf8.equals(fakeMHName)) {
 410                 utf8 = realMHName; didMHName = true;
 411             }
 412             if (utf8.equals(fakeInvokeName)) {
 413                 utf8 = realInvokeName; didInvokeName = true;
 414             }
 415             if ((Object)utf8 != orig)
 416                 patch.putUTF8(index, utf8);
 417         }
 418 
 419         public TemplateExpander(Class<? extends MethodHandleInvoker> template,
 420                                 MethodType approxType, MethodType exactType)
 421                 throws IOException, InvalidConstantPoolFormatException {
 422             // construct a descriptor of something like:
 423             //   int fake_invoke_I2(MethodHandle, Object, Object);
 424             fakeInvokeName = FMHInvokeName(approxType);
 425             cp = new ConstantPoolParser(template);
 426             patch = cp.createPatch();
 427             cp.parse(this);
 428             if (!(didMHName && didInvokeName))
 429                 throw new RuntimeException("utf8 rewrites failed: "
 430                     +(!didMHName?"":fakeMHName)+(!didInvokeName?"":fakeInvokeName));
 431         }
 432     }
 433 
 434     static final IdentityHashMap<MethodType,MethodHandleInvoker> invokers
 435             = new IdentityHashMap<MethodType, MethodHandleInvoker>();
 436 
 437     private static Class<? extends MethodHandleInvoker>
 438             expandTemplate(Class<? extends MethodHandleInvoker> template,
 439                            MethodType approxType, MethodType exactType)
 440                            throws IOException, InvalidConstantPoolFormatException {
 441         TemplateExpander tex = new TemplateExpander(template, approxType, exactType);
 442         return LOADER.loadClass(tex.patch).asSubclass(MethodHandleInvoker.class);
 443     }
 444 
 445     /** Throw this if a bad entry point is taken. */
 446     protected RuntimeException wrongType(MethodHandle mh) {
 447         return new WrongMethodTypeException("wrong call type for "+mh+
 448                 " should be "+exactType+" in "+this);
 449     }
 450     protected void checkType(MethodHandle mh) {
 451         if (mh.type() != exactType)
 452             throw wrongType(mh);
 453     }
 454 }