< prev index next >

jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormBuilder.java

Print this page




   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 jdk.experimental.bytecode.AnnotationsBuilder;
  29 import jdk.experimental.bytecode.MacroCodeBuilder.FieldAccessKind;
  30 import jdk.experimental.bytecode.MacroCodeBuilder.InvocationKind;
  31 import jdk.experimental.bytecode.MethodBuilder;
  32 import jdk.experimental.bytecode.TypeTag;
  33 import jdk.experimental.value.MethodHandleBuilder;


  34 import sun.invoke.util.VerifyType;
  35 import sun.invoke.util.Wrapper;
  36 import valhalla.shady.MinimalValueTypes_1_0;
  37 
  38 import java.lang.invoke.LambdaForm.BasicType;
  39 import java.lang.invoke.LambdaForm.Name;
  40 import java.lang.invoke.MethodHandles.Lookup;


  41 


  42 import static java.lang.invoke.LambdaForm.BasicType.L_TYPE;
  43 import static java.lang.invoke.LambdaForm.BasicType.V_TYPE;
  44 import static java.lang.invoke.LambdaForm.BasicType.basicType;
  45 import static java.lang.invoke.MethodHandleNatives.Constants.REF_getField;
  46 import static java.lang.invoke.MethodHandleNatives.Constants.REF_getStatic;
  47 import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeInterface;
  48 import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeSpecial;
  49 import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeStatic;
  50 import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeVirtual;
  51 import static java.lang.invoke.MethodHandleNatives.Constants.REF_putField;
  52 import static java.lang.invoke.MethodHandleNatives.Constants.REF_putStatic;

  53 import static java.lang.invoke.MethodHandleStatics.PROFILE_LEVEL;
  54 import static java.lang.invoke.MethodHandleStatics.newInternalError;

  55 
  56 /**
  57  * Utility class for spinning classfiles associated with lambda forms.
  58  */
  59 class LambdaFormBuilder extends MethodHandleBuilder {
  60 
  61     private static final String OBJ     = "java/lang/Object";
  62     private static final String CLASS_PREFIX   = "java/lang/invoke/LambdaForm$Value$";
  63     private static final String DEFAULT_CLASS  = "MH";
  64     private static final String LL_SIG  = "(L" + OBJ + ";)L" + OBJ + ";";
  65 



  66     static MemberName generateCustomizedCode(LambdaForm form, MethodType invokerType) {
  67         String invokerName = form.lambdaName();
  68         int p = invokerName.indexOf('.');
  69         boolean overrideNames = p != -1;
  70         String methodName = overrideNames ? invokerName.substring(p + 1) : invokerName;
  71         String className = overrideNames ?
  72                 CLASS_PREFIX + invokerName.substring(0, p) :
  73                 CLASS_PREFIX + DEFAULT_CLASS;
  74         if (MinimalValueTypes_1_0.DUMP_CLASS_FILES) {
  75             // When DUMP_CLASS_FILES is true methodName will have a unique id
  76             className = className + "_" + methodName;
  77         }
  78         return MethodHandleBuilder.loadCode(Lookup.IMPL_LOOKUP.in(LambdaForm.class), className, methodName, invokerType.toMethodDescriptorString(),
  79                 M -> new LambdaFormCodeBuilder(form, M), clazz -> InvokerBytecodeGenerator.resolveInvokerMember(clazz, methodName, invokerType),
  80                 C -> new LambdaFormBuilder(C, form, invokerType).generateLambdaFormBody());
  81     }
  82 
  83     LambdaFormCodeBuilder builder;
  84     LambdaForm lambdaForm;
  85     MethodType invokerType;


  86 
  87     LambdaFormBuilder(LambdaFormCodeBuilder builder, LambdaForm lambdaForm, MethodType invokerType) {
  88         this.builder = builder;
  89         this.lambdaForm = lambdaForm;
  90         this.invokerType = invokerType;


  91     }
  92 












  93     void generateLambdaFormBody() {
  94         // iterate over the form's names, generating bytecode instructions for each
  95         // start iterating at the first name following the arguments
  96         Name onStack = null;
  97         for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) {
  98             Name name = lambdaForm.names[i];
  99 
 100             if (onStack != null && onStack.type != V_TYPE) {
 101                 // non-void: actually assign
 102                 builder.store(fromBasicType(onStack.type), onStack.index());
 103             }
 104             onStack = name;  // unless otherwise modified below
 105             MemberName member = name.function.member();
 106             if (InvokerBytecodeGenerator.isStaticallyInvocable(member)) {














































 107                 emitStaticInvoke(member, name);
 108             } else {
 109                 emitInvoke(name);
 110             }
 111         }
 112         emitReturn(onStack);
 113     }
 114 
 115     /**
 116      * Emits a return statement from a LF invoker. If required, the result type is cast to the correct return type.
 117      */
 118     private void emitReturn(Name onStack) {
 119         // return statement
 120         Class<?> rclass = invokerType.returnType();
 121         BasicType rtype = lambdaForm.returnType();
 122         assert(rtype == basicType(rclass));  // must agree
 123         if (rtype == V_TYPE) {
 124             // void
 125             builder.return_();
 126             // it doesn't matter what rclass is; the JVM will discard any value
 127         } else {
 128             LambdaForm.Name rn = lambdaForm.names[lambdaForm.result];
 129 
 130             // put return value on the stack if it is not already there
 131             if (rn != onStack) {
 132                 builder.load(lambdaForm.result);
 133             }
 134 
 135             emitImplicitConversion(rtype, rclass, rn);
 136 
 137             // generate actual return statement
 138             builder.return_(fromBasicType(rtype));
 139         }
 140     }
 141 
 142     /**
 143      * Emit an implicit conversion for an argument which must be of the given pclass.
 144      * This is usually a no-op, except when pclass is a subword type or a reference other than Object or an interface.
 145      *
 146      * @param ptype type of value present on stack
 147      * @param pclass type of value required on stack
 148      * @param arg compile-time representation of value on stack (Node, constant) or null if none
 149      */
 150     private void emitImplicitConversion(BasicType ptype, Class<?> pclass, Object arg) {
 151         assert(basicType(pclass) == ptype);  // boxing/unboxing handled by caller
 152         if (pclass == ptype.basicTypeClass() && ptype != L_TYPE)


 162                 return;
 163             case I_TYPE:
 164                 if (!VerifyType.isNullConversion(int.class, pclass, false))
 165                     builder.conv(fromBasicType(ptype), fromBasicType(BasicType.basicType(pclass)));
 166                 return;
 167             case Q_TYPE:
 168                 if (!MinimalValueTypes_1_0.isValueType(pclass)) {
 169                     builder.vbox(Object.class);
 170                     return;
 171                 }
 172                 assert pclass == arg.getClass();
 173                 return; //assume they are equal
 174         }
 175         throw newInternalError("bad implicit conversion: tc="+ptype+": "+pclass);
 176     }
 177 
 178     private void emitReferenceCast(Class<?> cls, Object arg) {
 179         Name writeBack = null;  // local to write back result
 180         if (arg instanceof Name) {
 181             Name n = (Name) arg;
 182             if (cls.isAssignableFrom(builder.typeOfLocal(n.index())))
 183                 return;  // this cast was already performed
 184             if (lambdaForm.useCount(n) > 1) {
 185                 // This guy gets used more than once.
 186                 writeBack = n;
 187             }
 188         }
 189         if (InvokerBytecodeGenerator.isStaticallyNameable(cls)) {
 190             builder.checkcast(cls);
 191         } else {
 192             builder.ldc(cls)
 193                     .checkcast(Class.class)
 194                     .swap()
 195                     .invokevirtual(Class.class, "cast", LL_SIG, false);
 196             if (Object[].class.isAssignableFrom(cls))
 197                 builder.checkcast(Object[].class);
 198             else if (PROFILE_LEVEL > 0)
 199                 builder.checkcast(Object.class);
 200         }
 201         if (writeBack != null) {
 202             builder.dup().astore(writeBack.index());
 203         }
 204     }
 205 




 206     /**
 207      * Emit an invoke for the given name, using the MemberName directly.
 208      */
 209     void emitStaticInvoke(MemberName member, Name name) {
 210         assert(member.equals(name.function.member()));
 211         Class<?> defc = member.getDeclaringClass();
 212         String mname = member.getName();
 213         String mtype;
 214         byte refKind = member.getReferenceKind();
 215         if (refKind == REF_invokeSpecial) {
 216             // in order to pass the verifier, we need to convert this to invokevirtual in all cases
 217             assert(member.canBeStaticallyBound()) : member;
 218             refKind = REF_invokeVirtual;
 219         }
 220 
 221         assert(!(member.getDeclaringClass().isInterface() && refKind == REF_invokeVirtual));
 222 
 223         // push arguments
 224         emitPushArguments(name);
 225 


 263     private void emitPushArguments(Name args) {
 264         emitPushArguments(args, 0);
 265     }
 266 
 267     private void emitPushArguments(Name args, int start) {
 268         for (int i = start; i < args.arguments.length; i++) {
 269             emitPushArgument(args, i);
 270         }
 271     }
 272 
 273     private void emitPushArgument(Name name, int paramIndex) {
 274         Object arg = name.arguments[paramIndex];
 275         Class<?> ptype = name.function.methodType().parameterType(paramIndex);
 276         emitPushArgument(ptype, arg);
 277     }
 278 
 279     private void emitPushArgument(Class<?> ptype, Object arg) {
 280         BasicType bptype = basicType(ptype);
 281         if (arg instanceof Name) {
 282             Name n = (Name) arg;
 283             builder.load(fromBasicType(n.type), n.index());
 284             emitImplicitConversion(n.type, ptype, n);
 285         } else if ((arg == null || arg instanceof String) && bptype == L_TYPE) {
 286             builder.ldc(arg);
 287         } else {
 288             if (Wrapper.isWrapperType(arg.getClass()) && bptype != L_TYPE) {
 289                 builder.ldc(arg);
 290             } else {
 291                 builder.ldc(arg);
 292                 emitImplicitConversion(L_TYPE, ptype, arg);
 293             }
 294         }
 295     }
 296 
 297     TypeTag fromBasicType(BasicType type) {
 298         switch (type) {
 299             case I_TYPE:  return TypeTag.I;
 300             case J_TYPE:  return TypeTag.J;
 301             case F_TYPE:  return TypeTag.F;
 302             case D_TYPE:  return TypeTag.D;
 303             case L_TYPE:  return TypeTag.A;
 304             case V_TYPE:  return TypeTag.V;
 305             case Q_TYPE:  return TypeTag.Q;
 306             default:
 307                 throw new InternalError("unknown type: " + type);
 308         }
 309     }
 310 




 311     InvocationKind invKindFromRefKind(int refKind) {
 312         switch (refKind) {
 313             case REF_invokeVirtual:      return InvocationKind.INVOKEVIRTUAL;
 314             case REF_invokeStatic:       return InvocationKind.INVOKESTATIC;
 315             case REF_invokeSpecial:      return InvocationKind.INVOKESPECIAL;
 316             case REF_invokeInterface:    return InvocationKind.INVOKEINTERFACE;
 317         }
 318         throw new InternalError("refKind="+refKind);
 319     }
 320 
 321     FieldAccessKind fieldKindFromRefKind(int refKind) {
 322         switch (refKind) {
 323             case REF_getField:
 324             case REF_putField:            return FieldAccessKind.INSTANCE;
 325             case REF_getStatic:
 326             case REF_putStatic:          return FieldAccessKind.STATIC;
 327         }
 328         throw new InternalError("refKind="+refKind);
 329     }
 330 
 331     static class LambdaFormCodeBuilder extends MethodHandleCodeBuilder {
 332         public LambdaFormCodeBuilder(LambdaForm form, MethodBuilder<Class<?>, String, byte[]> methodBuilder) {
 333             super(methodBuilder);
 334             if (form.forceInline) {
 335                 methodBuilder.withAnnotation(AnnotationsBuilder.Kind.RUNTIME_VISIBLE, "Ljdk/internal/vm/annotation/ForceInline;");
 336             }
 337             methodBuilder.withAnnotation(AnnotationsBuilder.Kind.RUNTIME_VISIBLE, "Ljava/lang/invoke/LambdaForm$Hidden;")
 338                     .withAnnotation(AnnotationsBuilder.Kind.RUNTIME_VISIBLE, "Ljava/lang/invoke/LambdaForm$Compiled;");
 339         }
 340 
 341         Class<?> typeOfLocal(int index) {
 342             return typeHelper.symbol(state.locals.get(index));
 343         }
 344     }

























































































































































































 345 }


   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 jdk.experimental.bytecode.*;
  29 import jdk.experimental.bytecode.MacroCodeBuilder.FieldAccessKind;
  30 import jdk.experimental.bytecode.MacroCodeBuilder.InvocationKind;


  31 import jdk.experimental.value.MethodHandleBuilder;
  32 import jdk.internal.org.objectweb.asm.Label;
  33 import jdk.internal.org.objectweb.asm.Opcodes;
  34 import sun.invoke.util.VerifyType;
  35 import sun.invoke.util.Wrapper;
  36 import valhalla.shady.MinimalValueTypes_1_0;
  37 
  38 import java.lang.invoke.LambdaForm.BasicType;
  39 import java.lang.invoke.LambdaForm.Name;
  40 import java.lang.invoke.MethodHandles.Lookup;
  41 import java.util.Arrays;
  42 import java.util.stream.Stream;
  43 
  44 import static java.lang.invoke.InvokerBytecodeGenerator.isStaticallyInvocable;
  45 import static java.lang.invoke.InvokerBytecodeGenerator.isStaticallyNameable;
  46 import static java.lang.invoke.LambdaForm.BasicType.L_TYPE;
  47 import static java.lang.invoke.LambdaForm.BasicType.V_TYPE;
  48 import static java.lang.invoke.LambdaForm.BasicType.basicType;
  49 import static java.lang.invoke.MethodHandleNatives.Constants.REF_getField;
  50 import static java.lang.invoke.MethodHandleNatives.Constants.REF_getStatic;
  51 import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeInterface;
  52 import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeSpecial;
  53 import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeStatic;
  54 import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeVirtual;
  55 import static java.lang.invoke.MethodHandleNatives.Constants.REF_putField;
  56 import static java.lang.invoke.MethodHandleNatives.Constants.REF_putStatic;
  57 import static java.lang.invoke.MethodHandleStatics.PROFILE_GWT;
  58 import static java.lang.invoke.MethodHandleStatics.PROFILE_LEVEL;
  59 import static java.lang.invoke.MethodHandleStatics.newInternalError;
  60 import static jdk.experimental.bytecode.MacroCodeBuilder.CondKind.NE;
  61 
  62 /**
  63  * Utility class for spinning classfiles associated with lambda forms.
  64  */
  65 class LambdaFormBuilder extends MethodHandleBuilder {
  66 
  67     private static final String OBJ     = "java/lang/Object";
  68     private static final String CLASS_PREFIX   = "java/lang/invoke/LambdaForm$Value$";
  69     private static final String DEFAULT_CLASS  = "MH";
  70     private static final String LL_SIG  = "(L" + OBJ + ";)L" + OBJ + ";";
  71 
  72     private static final String MH           = "java/lang/invoke/MethodHandle";
  73     private static final String MHARY2       = "[[L" + MH + ";";
  74 
  75     static MemberName generateCustomizedCode(LambdaForm form, MethodType invokerType) {
  76         String invokerName = form.lambdaName();
  77         int p = invokerName.indexOf('.');
  78         boolean overrideNames = p != -1;
  79         String methodName = overrideNames ? invokerName.substring(p + 1) : invokerName;
  80         String className = overrideNames ?
  81                 CLASS_PREFIX + invokerName.substring(0, p) :
  82                 CLASS_PREFIX + DEFAULT_CLASS;
  83         if (MinimalValueTypes_1_0.DUMP_CLASS_FILES) {
  84             // When DUMP_CLASS_FILES is true methodName will have a unique id
  85             className = className + "_" + methodName;
  86         }
  87         return MethodHandleBuilder.loadCode(Lookup.IMPL_LOOKUP.in(LambdaForm.class), className, methodName, invokerType.toMethodDescriptorString(),
  88                 M -> new LambdaFormCodeBuilder(form, M), clazz -> InvokerBytecodeGenerator.resolveInvokerMember(clazz, methodName, invokerType),
  89                 C -> new LambdaFormBuilder(C, form, invokerType).generateLambdaFormBody());
  90     }
  91 
  92     LambdaFormCodeBuilder builder;
  93     LambdaForm lambdaForm;
  94     MethodType invokerType;
  95     int maxLocals;
  96     int[] localsMap;
  97 
  98     LambdaFormBuilder(LambdaFormCodeBuilder builder, LambdaForm lambdaForm, MethodType invokerType) {
  99         this.builder = builder;
 100         this.lambdaForm = lambdaForm;
 101         this.invokerType = invokerType;
 102         this.maxLocals = lambdaForm.names.length;
 103         this.localsMap = computeLocalsMap(lambdaForm);
 104     }
 105 
 106     static int[] computeLocalsMap(LambdaForm lform) {
 107         int localsMapSize = lform.names.length;
 108         int[] localsMap = new int[localsMapSize+1]; // last entry of localsMap is count of allocated local slots
 109         for (int i = 0, index = 0; i < localsMap.length; i++) {
 110             localsMap[i] = index;
 111             if (i < lform.names.length) {
 112                 BasicType type = lform.names[i].type();
 113                 index += type.basicTypeSlots();
 114             }
 115         }
 116         return localsMap;
 117     }
 118     void generateLambdaFormBody() {
 119         // iterate over the form's names, generating bytecode instructions for each
 120         // start iterating at the first name following the arguments
 121         Name onStack = null;
 122         for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) {
 123             Name name = lambdaForm.names[i];
 124 
 125             if (onStack != null && onStack.type != V_TYPE) {
 126                 // non-void: actually assign
 127                 builder.store(fromBasicType(onStack.type), localsMap[onStack.index()]);
 128             }
 129             onStack = name;  // unless otherwise modified below
 130             MemberName member = name.function.member();
 131             MethodHandleImpl.Intrinsic intr = name.function.intrinsicName();
 132             switch (intr) {
 133                  case SELECT_ALTERNATIVE: {
 134                      assert lambdaForm.isSelectAlternative(i);
 135                      if (PROFILE_GWT) {
 136                          assert(name.arguments[0] instanceof Name &&
 137                                  ((Name)name.arguments[0]).refersTo(MethodHandleImpl.class, "profileBoolean"));
 138 //                          mv.visitAnnotation(INJECTEDPROFILE_SIG, true);
 139                      }
 140                      onStack = emitSelectAlternative(name, lambdaForm.names[i+1]);
 141                      i++;  // skip MH.invokeBasic of the selectAlternative result
 142                      continue;
 143 
 144                  }
 145                 // case GUARD_WITH_CATCH:
 146                 // case TRY_FINALLY:
 147 
 148                 case LOOP: {
 149                     assert lambdaForm.isLoop(i);
 150                     onStack = emitLoop(i);
 151                     i += 2; // jump to the end of the LOOP idiom
 152                     continue;
 153                 }
 154                 case IDENTITY: {
 155                     assert (name.arguments.length == 1);
 156                     emitPushArguments(name, 0);
 157                     continue;
 158                 }
 159                 case ZERO: {
 160                     assert (name.arguments.length == 0);
 161                     assert (name.type != BasicType.Q_TYPE);
 162                     builder.ldc(name.type.basicTypeWrapper().zero());
 163                     continue;
 164                 }
 165                 case NONE: {
 166                     // no intrinsic associated
 167                     break;
 168                 }
 169                 // case NEW_ARRAY: ?
 170                 // case ARRAY_LOAD: -
 171                 // case ARRAY_STORE: -
 172                 // case ARRAY_LENGTH: -
 173                 default: {
 174                     throw newInternalError("Unknown intrinsic: "+intr);
 175                 }
 176             }
 177             if (isStaticallyInvocable(member)) {
 178                 emitStaticInvoke(member, name);
 179             } else {
 180                 emitInvoke(name);
 181             }
 182         }
 183         emitReturn(onStack);
 184     }
 185 
 186     /**
 187      * Emits a return statement from a LF invoker. If required, the result type is cast to the correct return type.
 188      */
 189     private void emitReturn(Name onStack) {
 190         // return statement
 191         Class<?> rclass = invokerType.returnType();
 192         BasicType rtype = lambdaForm.returnType();
 193         assert(rtype == basicType(rclass));  // must agree
 194         if (rtype == V_TYPE) {
 195             // void
 196             builder.return_();
 197             // it doesn't matter what rclass is; the JVM will discard any value
 198         } else {
 199             LambdaForm.Name rn = lambdaForm.names[lambdaForm.result];
 200 
 201             // put return value on the stack if it is not already there
 202             if (rn != onStack) {
 203                 builder.load(localsMap[lambdaForm.result]);
 204             }
 205 
 206             emitImplicitConversion(rtype, rclass, rn);
 207 
 208             // generate actual return statement
 209             builder.return_(fromBasicType(rtype));
 210         }
 211     }
 212 
 213     /**
 214      * Emit an implicit conversion for an argument which must be of the given pclass.
 215      * This is usually a no-op, except when pclass is a subword type or a reference other than Object or an interface.
 216      *
 217      * @param ptype type of value present on stack
 218      * @param pclass type of value required on stack
 219      * @param arg compile-time representation of value on stack (Node, constant) or null if none
 220      */
 221     private void emitImplicitConversion(BasicType ptype, Class<?> pclass, Object arg) {
 222         assert(basicType(pclass) == ptype);  // boxing/unboxing handled by caller
 223         if (pclass == ptype.basicTypeClass() && ptype != L_TYPE)


 233                 return;
 234             case I_TYPE:
 235                 if (!VerifyType.isNullConversion(int.class, pclass, false))
 236                     builder.conv(fromBasicType(ptype), fromBasicType(BasicType.basicType(pclass)));
 237                 return;
 238             case Q_TYPE:
 239                 if (!MinimalValueTypes_1_0.isValueType(pclass)) {
 240                     builder.vbox(Object.class);
 241                     return;
 242                 }
 243                 assert pclass == arg.getClass();
 244                 return; //assume they are equal
 245         }
 246         throw newInternalError("bad implicit conversion: tc="+ptype+": "+pclass);
 247     }
 248 
 249     private void emitReferenceCast(Class<?> cls, Object arg) {
 250         Name writeBack = null;  // local to write back result
 251         if (arg instanceof Name) {
 252             Name n = (Name) arg;
 253             if (cls.isAssignableFrom(builder.typeOfLocal(localsMap[n.index()])))
 254                 return;  // this cast was already performed
 255             if (lambdaForm.useCount(n) > 1) {
 256                 // This guy gets used more than once.
 257                 writeBack = n;
 258             }
 259         }
 260         if (isStaticallyNameable(cls)) {
 261             builder.checkcast(cls);
 262         } else {
 263             builder.ldc(cls)
 264                     .checkcast(Class.class)
 265                     .swap()
 266                     .invokevirtual(Class.class, "cast", LL_SIG, false);
 267             if (Object[].class.isAssignableFrom(cls))
 268                 builder.checkcast(Object[].class);
 269             else if (PROFILE_LEVEL > 0)
 270                 builder.checkcast(Object.class);
 271         }
 272         if (writeBack != null) {
 273             builder.dup().astore(localsMap[writeBack.index()]);
 274         }
 275     }
 276 
 277     void emitStaticInvoke(Name name) {
 278         emitStaticInvoke(name.function.member(), name);
 279     }
 280 
 281     /**
 282      * Emit an invoke for the given name, using the MemberName directly.
 283      */
 284     void emitStaticInvoke(MemberName member, Name name) {
 285         assert(member.equals(name.function.member()));
 286         Class<?> defc = member.getDeclaringClass();
 287         String mname = member.getName();
 288         String mtype;
 289         byte refKind = member.getReferenceKind();
 290         if (refKind == REF_invokeSpecial) {
 291             // in order to pass the verifier, we need to convert this to invokevirtual in all cases
 292             assert(member.canBeStaticallyBound()) : member;
 293             refKind = REF_invokeVirtual;
 294         }
 295 
 296         assert(!(member.getDeclaringClass().isInterface() && refKind == REF_invokeVirtual));
 297 
 298         // push arguments
 299         emitPushArguments(name);
 300 


 338     private void emitPushArguments(Name args) {
 339         emitPushArguments(args, 0);
 340     }
 341 
 342     private void emitPushArguments(Name args, int start) {
 343         for (int i = start; i < args.arguments.length; i++) {
 344             emitPushArgument(args, i);
 345         }
 346     }
 347 
 348     private void emitPushArgument(Name name, int paramIndex) {
 349         Object arg = name.arguments[paramIndex];
 350         Class<?> ptype = name.function.methodType().parameterType(paramIndex);
 351         emitPushArgument(ptype, arg);
 352     }
 353 
 354     private void emitPushArgument(Class<?> ptype, Object arg) {
 355         BasicType bptype = basicType(ptype);
 356         if (arg instanceof Name) {
 357             Name n = (Name) arg;
 358             builder.load(fromBasicType(n.type), localsMap[n.index()]);
 359             emitImplicitConversion(n.type, ptype, n);
 360         } else if ((arg == null || arg instanceof String) && bptype == L_TYPE) {
 361             builder.ldc(arg);
 362         } else {
 363             if (Wrapper.isWrapperType(arg.getClass()) && bptype != L_TYPE) {
 364                 builder.ldc(arg);
 365             } else {
 366                 builder.ldc(arg);
 367                 emitImplicitConversion(L_TYPE, ptype, arg);
 368             }
 369         }
 370     }
 371 
 372     TypeTag fromBasicType(BasicType type) {
 373         switch (type) {
 374             case I_TYPE:  return TypeTag.I;
 375             case J_TYPE:  return TypeTag.J;
 376             case F_TYPE:  return TypeTag.F;
 377             case D_TYPE:  return TypeTag.D;
 378             case L_TYPE:  return TypeTag.A;
 379             case V_TYPE:  return TypeTag.V;
 380             case Q_TYPE:  return TypeTag.Q;
 381             default:
 382                 throw new InternalError("unknown type: " + type);
 383         }
 384     }
 385 
 386     TypeTag fromClass(Class<?> cls) {
 387         return fromBasicType(BasicType.basicType(cls));
 388     }
 389 
 390     InvocationKind invKindFromRefKind(int refKind) {
 391         switch (refKind) {
 392             case REF_invokeVirtual:      return InvocationKind.INVOKEVIRTUAL;
 393             case REF_invokeStatic:       return InvocationKind.INVOKESTATIC;
 394             case REF_invokeSpecial:      return InvocationKind.INVOKESPECIAL;
 395             case REF_invokeInterface:    return InvocationKind.INVOKEINTERFACE;
 396         }
 397         throw new InternalError("refKind="+refKind);
 398     }
 399 
 400     FieldAccessKind fieldKindFromRefKind(int refKind) {
 401         switch (refKind) {
 402             case REF_getField:
 403             case REF_putField:            return FieldAccessKind.INSTANCE;
 404             case REF_getStatic:
 405             case REF_putStatic:          return FieldAccessKind.STATIC;
 406         }
 407         throw new InternalError("refKind="+refKind);
 408     }
 409 
 410     static class LambdaFormCodeBuilder extends MethodHandleCodeBuilder {
 411         public LambdaFormCodeBuilder(LambdaForm form, MethodBuilder<Class<?>, String, byte[]> methodBuilder) {
 412             super(methodBuilder);
 413             if (form.forceInline) {
 414                 methodBuilder.withAnnotation(AnnotationsBuilder.Kind.RUNTIME_VISIBLE, "Ljdk/internal/vm/annotation/ForceInline;");
 415             }
 416             methodBuilder.withAnnotation(AnnotationsBuilder.Kind.RUNTIME_VISIBLE, "Ljava/lang/invoke/LambdaForm$Hidden;")
 417                     .withAnnotation(AnnotationsBuilder.Kind.RUNTIME_VISIBLE, "Ljava/lang/invoke/LambdaForm$Compiled;");
 418         }
 419 
 420         Class<?> typeOfLocal(int index) {
 421             return typeHelper.symbol(state.locals.get(index));
 422         }
 423     }
 424 
 425     private Name emitLoop(int pos) {
 426         Name args    = lambdaForm.names[pos];
 427         Name invoker = lambdaForm.names[pos+1];
 428         Name result  = lambdaForm.names[pos+2];
 429 
 430         // extract clause and loop-local state types
 431         // find the type info in the loop invocation
 432         BasicType[] loopClauseTypes = (BasicType[]) invoker.arguments[0];
 433         Class<?>[] loopLocalStateTypes = Stream.of(loopClauseTypes).
 434                 filter(bt -> bt != BasicType.V_TYPE).map(BasicType::basicTypeClass).toArray(Class<?>[]::new);
 435 
 436         Class<?>[] localTypes = new Class<?>[loopLocalStateTypes.length + 1];
 437         localTypes[0] = MethodHandleImpl.LoopClauses.class;
 438         System.arraycopy(loopLocalStateTypes, 0, localTypes, 1, loopLocalStateTypes.length);
 439 
 440         final int clauseDataIndex     = extendLocalsMap(localTypes);
 441         final int firstLoopStateIndex = clauseDataIndex + 1;
 442         maxLocals += loopLocalStateTypes.length + 1;
 443 
 444         Class<?> returnType = result.function.resolvedHandle().type().returnType();
 445         MethodType loopType = args.function.resolvedHandle().type()
 446                 .dropParameterTypes(0,1)
 447                 .changeReturnType(returnType);
 448         MethodType loopHandleType = loopType.insertParameterTypes(0, loopLocalStateTypes);
 449         MethodType predType = loopHandleType.changeReturnType(boolean.class);
 450         MethodType finiType = loopHandleType;
 451 
 452         final int nClauses = loopClauseTypes.length;
 453 
 454         // indices to invoker arguments to load method handle arrays
 455         final int inits = 1;
 456         final int steps = 2;
 457         final int preds = 3;
 458         final int finis = 4;
 459 
 460         // PREINIT:
 461         emitPushArgument(MethodHandleImpl.LoopClauses.class, invoker.arguments[1]);
 462         builder.getfield(MethodHandleImpl.LoopClauses.class, "clauses", MHARY2);
 463         emitAstoreInsn(clauseDataIndex);
 464 
 465         // INIT:
 466         for (int c = 0, state = 0; c < nClauses; ++c) {
 467             MethodType cInitType = loopType.changeReturnType(loopClauseTypes[c].basicTypeClass());
 468             emitLoopHandleInvoke(inits, c, args, false, cInitType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex);
 469             if (cInitType.returnType() != void.class) {
 470                 builder.store(fromClass(cInitType.returnType()), localsMap[firstLoopStateIndex + state]);
 471                 ++state;
 472             }
 473         }
 474 
 475         // LOOP:
 476         builder.label("LOOP");
 477 
 478         String val = null;
 479         for (int c = 0, s = 0; c < nClauses; ++c) {
 480             MethodType stepType = loopHandleType.changeReturnType(loopClauseTypes[c].basicTypeClass());
 481             boolean isVoid = (stepType.returnType() == void.class);
 482 
 483             // invoke loop step
 484             emitLoopHandleInvoke(steps, c, args, true, stepType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex);
 485             if (!isVoid) {
 486                 builder.store(fromClass(stepType.returnType()), localsMap[firstLoopStateIndex + s]);
 487                 ++s;
 488             }
 489 
 490             // invoke loop predicate
 491             emitLoopHandleInvoke(preds, c, args, true, predType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex);
 492             builder.emitCondJump(Opcode.IFEQ, NE, "NEXT_" + c);
 493 
 494             // invoke fini
 495             emitLoopHandleInvoke(finis, c, args, true, finiType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex);
 496             builder.goto_("DONE");
 497 
 498             if (finiType.returnType() != void.class) {
 499                 val = builder.state().pop(); // FIXME !!! HACK !!!
 500             }
 501 
 502             // this is the beginning of the next loop clause
 503             builder.label("NEXT_" + c);
 504         }
 505         builder.goto_("LOOP");
 506 
 507         if (finiType.returnType() != void.class) {
 508             builder.state().push(val); // FIXME !!! HACK !!!
 509         }
 510 
 511         builder.label("DONE");
 512 
 513         return result;
 514     }
 515 
 516     private void emitLoopHandleInvoke(int handles, int clause, Name args, boolean pushLocalState,
 517                                       MethodType type, Class<?>[] loopLocalStateTypes, int clauseDataSlot,
 518                                       int firstLoopStateSlot) {
 519         // load handle for clause
 520 //        emitPushClauseArray(clauseDataSlot, handles);
 521         builder.load(TypeTag.A, localsMap[clauseDataSlot])
 522                .ldc(handles - 1)
 523                .aaload()
 524 //        emitIconstInsn(clause);
 525                .ldc(clause)
 526                .aaload();
 527 
 528         // load loop state (preceding the other arguments)
 529         if (pushLocalState) {
 530             for (int s = 0; s < loopLocalStateTypes.length; ++s) {
 531 //                emitLoadInsn(BasicType.basicType(loopLocalStateTypes[s]), firstLoopStateSlot + s);
 532                 builder.load(fromClass(loopLocalStateTypes[s]), localsMap[firstLoopStateSlot + s]);
 533             }
 534         }
 535         // load loop args (skip 0: method handle)
 536         emitPushArguments(args, 1);
 537         builder.invokevirtual(MethodHandle.class, "invokeBasic", type.toMethodDescriptorString(), false);
 538     }
 539 
 540     /**
 541      * Emit bytecode for the selectAlternative idiom.
 542      *
 543      * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest):
 544      * <blockquote><pre>{@code
 545      *   Lambda(a0:L,a1:I)=>{
 546      *     t2:I=foo.test(a1:I);
 547      *     t3:L=MethodHandleImpl.selectAlternative(t2:I,(MethodHandle(int)int),(MethodHandle(int)int));
 548      *     t4:I=MethodHandle.invokeBasic(t3:L,a1:I);t4:I}
 549      * }</pre></blockquote>
 550      */
 551     private Name emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) {
 552         assert isStaticallyInvocable(invokeBasicName);
 553 
 554         Name receiver = (Name) invokeBasicName.arguments[0];
 555 
 556         int pos = invokeBasicName.index();
 557         String L_fallback = "L_fallback_" + pos;
 558         String L_done     = "L_done_"+ pos;
 559 
 560         // load test result
 561         emitPushArgument(selectAlternativeName, 0);
 562 
 563         // if_icmpne L_fallback
 564         builder.emitCondJump(Opcode.IFEQ, NE, L_fallback);
 565 
 566         // invoke selectAlternativeName.arguments[1]
 567         emitPushArgument(selectAlternativeName, 1);  // get 2nd argument of selectAlternative
 568         emitAstoreInsn(receiver.index());  // store the MH in the receiver slot
 569         emitStaticInvoke(invokeBasicName);
 570 
 571         // goto L_done
 572         builder.goto_(L_done);
 573 
 574         if (invokeBasicName.type() != BasicType.V_TYPE) {
 575             builder.state().pop(); // FIXME !!! HACK !!!
 576         }
 577 
 578         // L_fallback:
 579         builder.label(L_fallback);
 580 
 581         // invoke selectAlternativeName.arguments[2]
 582         emitPushArgument(selectAlternativeName, 2);  // get 3rd argument of selectAlternative
 583         emitAstoreInsn(receiver.index());  // store the MH in the receiver slot
 584         emitStaticInvoke(invokeBasicName);
 585 
 586         // L_done:
 587         builder.label(L_done);
 588 
 589         return invokeBasicName;  // return what's on stack
 590     }
 591 
 592     private void emitAstoreInsn(int index) {
 593         builder.store(TypeTag.A, localsMap[index]);
 594     }
 595 
 596     private int extendLocalsMap(Class<?>[] types) {
 597         int firstSlot = localsMap.length - 1;
 598         localsMap = Arrays.copyOf(localsMap, localsMap.length + types.length);
 599         int index = localsMap[firstSlot - 1] + 1;
 600         int lastSlots = 0;
 601         for (int i = 0; i < types.length; ++i) {
 602             localsMap[firstSlot + i] = index;
 603             lastSlots = BasicType.basicType(types[i]).basicTypeSlots();
 604             index += lastSlots;
 605         }
 606         localsMap[localsMap.length - 1] = index - lastSlots;
 607         return firstSlot;
 608     }
 609 }
< prev index next >