rt/emul/compact/src/main/java/java/lang/invoke/BoundMethodHandle.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Sun, 10 Aug 2014 07:02:12 +0200
branchjdk8
changeset 1653 bd151459ee4f
parent 1646 c880a8a8803b
permissions -rw-r--r--
Capable to compile the java.lang.invoke stuff
     1 /*
     2  * Copyright (c) 2008, 2013, 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 java.lang.invoke;
    27 
    28 import static java.lang.invoke.LambdaForm.basicTypes;
    29 import static java.lang.invoke.MethodHandleStatics.*;
    30 
    31 import java.lang.invoke.LambdaForm.Name;
    32 import java.lang.invoke.LambdaForm.NamedFunction;
    33 import java.lang.invoke.MethodHandles.Lookup;
    34 import java.util.Arrays;
    35 import java.util.HashMap;
    36 
    37 import sun.invoke.util.ValueConversions;
    38 
    39 /**
    40  * The flavor of method handle which emulates an invoke instruction
    41  * on a predetermined argument.  The JVM dispatches to the correct method
    42  * when the handle is created, not when it is invoked.
    43  *
    44  * All bound arguments are encapsulated in dedicated species.
    45  */
    46 /* non-public */ abstract class BoundMethodHandle extends MethodHandle {
    47 
    48     /* non-public */ BoundMethodHandle(MethodType type, LambdaForm form) {
    49         super(type, form);
    50     }
    51 
    52     //
    53     // BMH API and internals
    54     //
    55 
    56     static MethodHandle bindSingle(MethodType type, LambdaForm form, char xtype, Object x) {
    57         // for some type signatures, there exist pre-defined concrete BMH classes
    58         try {
    59             switch (xtype) {
    60             case 'L':
    61                 if (true)  return bindSingle(type, form, x);  // Use known fast path.
    62                 return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('L').constructor[0].invokeBasic(type, form, x);
    63             case 'I':
    64                 return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('I').constructor[0].invokeBasic(type, form, ValueConversions.widenSubword(x));
    65             case 'J':
    66                 return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('J').constructor[0].invokeBasic(type, form, (long) x);
    67             case 'F':
    68                 return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('F').constructor[0].invokeBasic(type, form, (float) x);
    69             case 'D':
    70                 return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('D').constructor[0].invokeBasic(type, form, (double) x);
    71             default : throw new InternalError("unexpected xtype: " + xtype);
    72             }
    73         } catch (Throwable t) {
    74             throw newInternalError(t);
    75         }
    76     }
    77 
    78     static MethodHandle bindSingle(MethodType type, LambdaForm form, Object x) {
    79             return new Species_L(type, form, x);
    80     }
    81 
    82     MethodHandle cloneExtend(MethodType type, LambdaForm form, char xtype, Object x) {
    83         try {
    84             switch (xtype) {
    85             case 'L': return cloneExtendL(type, form, x);
    86             case 'I': return cloneExtendI(type, form, ValueConversions.widenSubword(x));
    87             case 'J': return cloneExtendJ(type, form, (long) x);
    88             case 'F': return cloneExtendF(type, form, (float) x);
    89             case 'D': return cloneExtendD(type, form, (double) x);
    90             }
    91         } catch (Throwable t) {
    92             throw newInternalError(t);
    93         }
    94         throw new InternalError("unexpected type: " + xtype);
    95     }
    96 
    97     @Override
    98     MethodHandle bindArgument(int pos, char basicType, Object value) {
    99         MethodType type = type().dropParameterTypes(pos, pos+1);
   100         LambdaForm form = internalForm().bind(1+pos, speciesData());
   101         return cloneExtend(type, form, basicType, value);
   102     }
   103 
   104     @Override
   105     MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
   106         LambdaForm form = internalForm().addArguments(pos, srcType.parameterList().subList(pos, pos+drops));
   107         try {
   108              return clone(srcType, form);
   109          } catch (Throwable t) {
   110              throw newInternalError(t);
   111          }
   112     }
   113 
   114     @Override
   115     MethodHandle permuteArguments(MethodType newType, int[] reorder) {
   116         try {
   117              return clone(newType, form.permuteArguments(1, reorder, basicTypes(newType.parameterList())));
   118          } catch (Throwable t) {
   119              throw newInternalError(t);
   120          }
   121     }
   122 
   123     static final String EXTENSION_TYPES = "LIJFD";
   124     static final byte INDEX_L = 0, INDEX_I = 1, INDEX_J = 2, INDEX_F = 3, INDEX_D = 4;
   125     static byte extensionIndex(char type) {
   126         int i = EXTENSION_TYPES.indexOf(type);
   127         if (i < 0)  throw new InternalError();
   128         return (byte) i;
   129     }
   130 
   131     /**
   132      * Return the {@link SpeciesData} instance representing this BMH species. All subclasses must provide a
   133      * static field containing this value, and they must accordingly implement this method.
   134      */
   135     protected abstract SpeciesData speciesData();
   136 
   137     @Override
   138     final Object internalProperties() {
   139         return "/BMH="+internalValues();
   140     }
   141 
   142     @Override
   143     final Object internalValues() {
   144         Object[] boundValues = new Object[speciesData().fieldCount()];
   145         for (int i = 0; i < boundValues.length; ++i) {
   146             boundValues[i] = arg(i);
   147         }
   148         return Arrays.asList(boundValues);
   149     }
   150 
   151     public final Object arg(int i) {
   152         try {
   153             switch (speciesData().fieldType(i)) {
   154             case 'L': return argL(i);
   155             case 'I': return argI(i);
   156             case 'F': return argF(i);
   157             case 'D': return argD(i);
   158             case 'J': return argJ(i);
   159             }
   160         } catch (Throwable ex) {
   161             throw newInternalError(ex);
   162         }
   163         throw new InternalError("unexpected type: " + speciesData().types+"."+i);
   164     }
   165     public final Object argL(int i) throws Throwable { return          speciesData().getters[i].invokeBasic(this); }
   166     public final int    argI(int i) throws Throwable { return (int)    speciesData().getters[i].invokeBasic(this); }
   167     public final float  argF(int i) throws Throwable { return (float)  speciesData().getters[i].invokeBasic(this); }
   168     public final double argD(int i) throws Throwable { return (double) speciesData().getters[i].invokeBasic(this); }
   169     public final long   argJ(int i) throws Throwable { return (long)   speciesData().getters[i].invokeBasic(this); }
   170 
   171     //
   172     // cloning API
   173     //
   174 
   175     public abstract BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable;
   176     public abstract BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable;
   177     public abstract BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int    narg) throws Throwable;
   178     public abstract BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long   narg) throws Throwable;
   179     public abstract BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float  narg) throws Throwable;
   180     public abstract BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable;
   181 
   182     // The following is a grossly irregular hack:
   183     @Override MethodHandle reinvokerTarget() {
   184         try {
   185             return (MethodHandle) argL(0);
   186         } catch (Throwable ex) {
   187             throw newInternalError(ex);
   188         }
   189     }
   190 
   191     //
   192     // concrete BMH classes required to close bootstrap loops
   193     //
   194 
   195     private  // make it private to force users to access the enclosing class first
   196     static final class Species_L extends BoundMethodHandle {
   197         final Object argL0;
   198         public Species_L(MethodType mt, LambdaForm lf, Object argL0) {
   199             super(mt, lf);
   200             this.argL0 = argL0;
   201         }
   202         // The following is a grossly irregular hack:
   203         @Override MethodHandle reinvokerTarget() { return (MethodHandle) argL0; }
   204         @Override
   205         public SpeciesData speciesData() {
   206             return SPECIES_DATA;
   207         }
   208         public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("L", Species_L.class);
   209         @Override
   210         public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable {
   211             return new Species_L(mt, lf, argL0);
   212         }
   213         @Override
   214         public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
   215             return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argL0, narg);
   216         }
   217         @Override
   218         public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable {
   219             return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argL0, narg);
   220         }
   221         @Override
   222         public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable {
   223             return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argL0, narg);
   224         }
   225         @Override
   226         public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable {
   227             return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argL0, narg);
   228         }
   229         @Override
   230         public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable {
   231             return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argL0, narg);
   232         }
   233     }
   234 
   235 /*
   236     static final class Species_LL extends BoundMethodHandle {
   237         final Object argL0;
   238         final Object argL1;
   239         public Species_LL(MethodType mt, LambdaForm lf, Object argL0, Object argL1) {
   240             super(mt, lf);
   241             this.argL0 = argL0;
   242             this.argL1 = argL1;
   243         }
   244         @Override
   245         public SpeciesData speciesData() {
   246             return SPECIES_DATA;
   247         }
   248         public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("LL", Species_LL.class);
   249         @Override
   250         public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable {
   251             return new Species_LL(mt, lf, argL0, argL1);
   252         }
   253         @Override
   254         public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
   255             return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
   256         }
   257         @Override
   258         public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable {
   259             return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
   260         }
   261         @Override
   262         public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable {
   263             return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
   264         }
   265         @Override
   266         public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable {
   267             return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
   268         }
   269         @Override
   270         public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable {
   271             return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
   272         }
   273     }
   274 
   275     static final class Species_JL extends BoundMethodHandle {
   276         final long argJ0;
   277         final Object argL1;
   278         public Species_JL(MethodType mt, LambdaForm lf, long argJ0, Object argL1) {
   279             super(mt, lf);
   280             this.argJ0 = argJ0;
   281             this.argL1 = argL1;
   282         }
   283         @Override
   284         public SpeciesData speciesData() {
   285             return SPECIES_DATA;
   286         }
   287         public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("JL", Species_JL.class);
   288         @Override public final long   argJ0() { return argJ0; }
   289         @Override public final Object argL1() { return argL1; }
   290         @Override
   291         public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable {
   292             return new Species_JL(mt, lf, argJ0, argL1);
   293         }
   294         @Override
   295         public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
   296             return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
   297         }
   298         @Override
   299         public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable {
   300             return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
   301         }
   302         @Override
   303         public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable {
   304             return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
   305         }
   306         @Override
   307         public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable {
   308             return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
   309         }
   310         @Override
   311         public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable {
   312             return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
   313         }
   314     }
   315 */
   316 
   317     //
   318     // BMH species meta-data
   319     //
   320 
   321     /**
   322      * Meta-data wrapper for concrete BMH classes.
   323      */
   324     static class SpeciesData {
   325         final String                             types;
   326         final Class<? extends BoundMethodHandle> clazz;
   327         // Bootstrapping requires circular relations MH -> BMH -> SpeciesData -> MH
   328         // Therefore, we need a non-final link in the chain.  Use array elements.
   329         final MethodHandle[]                     constructor;
   330         final MethodHandle[]                     getters;
   331         final SpeciesData[]                      extensions;
   332 
   333         public int fieldCount() {
   334             return types.length();
   335         }
   336         public char fieldType(int i) {
   337             return types.charAt(i);
   338         }
   339 
   340         public String toString() {
   341             return "SpeciesData["+(isPlaceholder() ? "<placeholder>" : clazz.getSimpleName())+":"+types+"]";
   342         }
   343 
   344         /**
   345          * Return a {@link LambdaForm.Name} containing a {@link LambdaForm.NamedFunction} that
   346          * represents a MH bound to a generic invoker, which in turn forwards to the corresponding
   347          * getter.
   348          */
   349         Name getterName(Name mhName, int i) {
   350             MethodHandle mh = getters[i];
   351             assert(mh != null) : this+"."+i;
   352             return new Name(mh, mhName);
   353         }
   354 
   355         NamedFunction getterFunction(int i) {
   356             return new NamedFunction(getters[i]);
   357         }
   358 
   359         static final SpeciesData EMPTY = new SpeciesData("", BoundMethodHandle.class);
   360 
   361         private SpeciesData(String types, Class<? extends BoundMethodHandle> clazz) {
   362             this.types = types;
   363             this.clazz = clazz;
   364             if (!INIT_DONE) {
   365                 this.constructor = new MethodHandle[1];
   366                 this.getters = new MethodHandle[types.length()];
   367             } else {
   368                 throw new IllegalStateException("bound method handle");
   369 //                this.constructor = Factory.makeCtors(clazz, types, null);
   370 //                this.getters = Factory.makeGetters(clazz, types, null);
   371             }
   372             this.extensions = new SpeciesData[EXTENSION_TYPES.length()];
   373         }
   374 
   375         private void initForBootstrap() {
   376             assert(!INIT_DONE);
   377             if (constructor[0] == null) {
   378 //                Factory.makeCtors(clazz, types, this.constructor);
   379 //                Factory.makeGetters(clazz, types, this.getters);
   380             }
   381         }
   382 
   383         private SpeciesData(String types) {
   384             // Placeholder only.
   385             this.types = types;
   386             this.clazz = null;
   387             this.constructor = null;
   388             this.getters = null;
   389             this.extensions = null;
   390         }
   391         private boolean isPlaceholder() { return clazz == null; }
   392 
   393         private static final HashMap<String, SpeciesData> CACHE = new HashMap<>();
   394         static { CACHE.put("", EMPTY); }  // make bootstrap predictable
   395         private static final boolean INIT_DONE;  // set after <clinit> finishes...
   396 
   397         SpeciesData extendWithType(char type) {
   398             int i = extensionIndex(type);
   399             SpeciesData d = extensions[i];
   400             if (d != null)  return d;
   401             extensions[i] = d = get(types+type);
   402             return d;
   403         }
   404 
   405         SpeciesData extendWithIndex(byte index) {
   406             SpeciesData d = extensions[index];
   407             if (d != null)  return d;
   408             extensions[index] = d = get(types+EXTENSION_TYPES.charAt(index));
   409             return d;
   410         }
   411 
   412         private static SpeciesData get(String types) {
   413             // Acquire cache lock for query.
   414             SpeciesData d = lookupCache(types);
   415             if (!d.isPlaceholder())
   416                 return d;
   417             synchronized (d) {
   418                 // Use synch. on the placeholder to prevent multiple instantiation of one species.
   419                 // Creating this class forces a recursive call to getForClass.
   420                 if (lookupCache(types).isPlaceholder())
   421                     throw new IllegalStateException("Cannot generate anything");
   422             }
   423             // Reacquire cache lock.
   424             d = lookupCache(types);
   425             // Class loading must have upgraded the cache.
   426             assert(d != null && !d.isPlaceholder());
   427             return d;
   428         }
   429         static SpeciesData getForClass(String types, Class<? extends BoundMethodHandle> clazz) {
   430             // clazz is a new class which is initializing its SPECIES_DATA field
   431             return updateCache(types, new SpeciesData(types, clazz));
   432         }
   433         private static synchronized SpeciesData lookupCache(String types) {
   434             SpeciesData d = CACHE.get(types);
   435             if (d != null)  return d;
   436             d = new SpeciesData(types);
   437             assert(d.isPlaceholder());
   438             CACHE.put(types, d);
   439             return d;
   440         }
   441         private static synchronized SpeciesData updateCache(String types, SpeciesData d) {
   442             SpeciesData d2;
   443             assert((d2 = CACHE.get(types)) == null || d2.isPlaceholder());
   444             assert(!d.isPlaceholder());
   445             CACHE.put(types, d);
   446             return d;
   447         }
   448 
   449         static {
   450             // pre-fill the BMH speciesdata cache with BMH's inner classes
   451             final Class<BoundMethodHandle> rootCls = BoundMethodHandle.class;
   452             SpeciesData d0 = BoundMethodHandle.SPECIES_DATA;  // trigger class init
   453             assert(d0 == null || d0 == lookupCache("")) : d0;
   454             try {
   455                 /*
   456                 for (Class<?> c : rootCls.getDeclaredClasses()) {
   457                     if (rootCls.isAssignableFrom(c)) {
   458                         final Class<? extends BoundMethodHandle> cbmh = c.asSubclass(BoundMethodHandle.class);
   459                         SpeciesData d = Factory.speciesDataFromConcreteBMHClass(cbmh);
   460                         assert(d != null) : cbmh.getName();
   461                         assert(d.clazz == cbmh);
   462                         assert(d == lookupCache(d.types));
   463                     }
   464                 }
   465                 */
   466             } catch (Throwable e) {
   467                 throw newInternalError(e);
   468             }
   469 
   470             for (SpeciesData d : CACHE.values()) {
   471                 d.initForBootstrap();
   472             }
   473             // Note:  Do not simplify this, because INIT_DONE must not be
   474             // a compile-time constant during bootstrapping.
   475             INIT_DONE = Boolean.TRUE;
   476         }
   477     }
   478 
   479     static SpeciesData getSpeciesData(String types) {
   480         return SpeciesData.get(types);
   481     }
   482 
   483 
   484 
   485     private static final Lookup LOOKUP = Lookup.IMPL_LOOKUP;
   486 
   487     /**
   488      * All subclasses must provide such a value describing their type signature.
   489      */
   490     static final SpeciesData SPECIES_DATA = SpeciesData.EMPTY;
   491 }