rt/emul/compact/src/main/java/java/lang/invoke/TypeConvertingMethodAdapter.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Sat, 09 Aug 2014 11:11:13 +0200
branchjdk8-b132
changeset 1646 c880a8a8803b
permissions -rw-r--r--
Batch of classes necessary to implement invoke dynamic interfaces. Taken from JDK8 build 132
     1 /*
     2  * Copyright (c) 2012, 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 jdk.internal.org.objectweb.asm.MethodVisitor;
    29 import jdk.internal.org.objectweb.asm.Opcodes;
    30 import jdk.internal.org.objectweb.asm.Type;
    31 import sun.invoke.util.BytecodeDescriptor;
    32 import sun.invoke.util.Wrapper;
    33 import static sun.invoke.util.Wrapper.*;
    34 
    35 class TypeConvertingMethodAdapter extends MethodVisitor {
    36 
    37     TypeConvertingMethodAdapter(MethodVisitor mv) {
    38         super(Opcodes.ASM5, mv);
    39     }
    40 
    41     private static final int NUM_WRAPPERS = Wrapper.values().length;
    42 
    43     private static final String NAME_OBJECT = "java/lang/Object";
    44     private static final String WRAPPER_PREFIX = "Ljava/lang/";
    45 
    46     // Same for all primitives; name of the boxing method
    47     private static final String NAME_BOX_METHOD = "valueOf";
    48 
    49     // Table of opcodes for widening primitive conversions; NOP = no conversion
    50     private static final int[][] wideningOpcodes = new int[NUM_WRAPPERS][NUM_WRAPPERS];
    51 
    52     private static final Wrapper[] FROM_WRAPPER_NAME = new Wrapper[16];
    53 
    54     // Table of wrappers for primitives, indexed by ASM type sorts
    55     private static final Wrapper[] FROM_TYPE_SORT = new Wrapper[16];
    56 
    57     static {
    58         for (Wrapper w : Wrapper.values()) {
    59             if (w.basicTypeChar() != 'L') {
    60                 int wi = hashWrapperName(w.wrapperSimpleName());
    61                 assert (FROM_WRAPPER_NAME[wi] == null);
    62                 FROM_WRAPPER_NAME[wi] = w;
    63             }
    64         }
    65 
    66         for (int i = 0; i < NUM_WRAPPERS; i++) {
    67             for (int j = 0; j < NUM_WRAPPERS; j++) {
    68                 wideningOpcodes[i][j] = Opcodes.NOP;
    69             }
    70         }
    71 
    72         initWidening(LONG,   Opcodes.I2L, BYTE, SHORT, INT, CHAR);
    73         initWidening(LONG,   Opcodes.F2L, FLOAT);
    74         initWidening(FLOAT,  Opcodes.I2F, BYTE, SHORT, INT, CHAR);
    75         initWidening(FLOAT,  Opcodes.L2F, LONG);
    76         initWidening(DOUBLE, Opcodes.I2D, BYTE, SHORT, INT, CHAR);
    77         initWidening(DOUBLE, Opcodes.F2D, FLOAT);
    78         initWidening(DOUBLE, Opcodes.L2D, LONG);
    79 
    80         FROM_TYPE_SORT[Type.BYTE] = Wrapper.BYTE;
    81         FROM_TYPE_SORT[Type.SHORT] = Wrapper.SHORT;
    82         FROM_TYPE_SORT[Type.INT] = Wrapper.INT;
    83         FROM_TYPE_SORT[Type.LONG] = Wrapper.LONG;
    84         FROM_TYPE_SORT[Type.CHAR] = Wrapper.CHAR;
    85         FROM_TYPE_SORT[Type.FLOAT] = Wrapper.FLOAT;
    86         FROM_TYPE_SORT[Type.DOUBLE] = Wrapper.DOUBLE;
    87         FROM_TYPE_SORT[Type.BOOLEAN] = Wrapper.BOOLEAN;
    88     }
    89 
    90     private static void initWidening(Wrapper to, int opcode, Wrapper... from) {
    91         for (Wrapper f : from) {
    92             wideningOpcodes[f.ordinal()][to.ordinal()] = opcode;
    93         }
    94     }
    95 
    96     /**
    97      * Class name to Wrapper hash, derived from Wrapper.hashWrap()
    98      * @param xn
    99      * @return The hash code 0-15
   100      */
   101     private static int hashWrapperName(String xn) {
   102         if (xn.length() < 3) {
   103             return 0;
   104         }
   105         return (3 * xn.charAt(1) + xn.charAt(2)) % 16;
   106     }
   107 
   108     private Wrapper wrapperOrNullFromDescriptor(String desc) {
   109         if (!desc.startsWith(WRAPPER_PREFIX)) {
   110             // Not a class type (array or method), so not a boxed type
   111             // or not in the right package
   112             return null;
   113         }
   114         // Pare it down to the simple class name
   115         String cname = desc.substring(WRAPPER_PREFIX.length(), desc.length() - 1);
   116         // Hash to a Wrapper
   117         Wrapper w = FROM_WRAPPER_NAME[hashWrapperName(cname)];
   118         if (w == null || w.wrapperSimpleName().equals(cname)) {
   119             return w;
   120         } else {
   121             return null;
   122         }
   123     }
   124 
   125     private static String wrapperName(Wrapper w) {
   126         return "java/lang/" + w.wrapperSimpleName();
   127     }
   128 
   129     private static String unboxMethod(Wrapper w) {
   130         return w.primitiveSimpleName() + "Value";
   131     }
   132 
   133     private static String boxingDescriptor(Wrapper w) {
   134         return String.format("(%s)L%s;", w.basicTypeChar(), wrapperName(w));
   135     }
   136 
   137     private static String unboxingDescriptor(Wrapper w) {
   138         return "()" + w.basicTypeChar();
   139     }
   140 
   141     void boxIfTypePrimitive(Type t) {
   142         Wrapper w = FROM_TYPE_SORT[t.getSort()];
   143         if (w != null) {
   144             box(w);
   145         }
   146     }
   147 
   148     void widen(Wrapper ws, Wrapper wt) {
   149         if (ws != wt) {
   150             int opcode = wideningOpcodes[ws.ordinal()][wt.ordinal()];
   151             if (opcode != Opcodes.NOP) {
   152                 visitInsn(opcode);
   153             }
   154         }
   155     }
   156 
   157     void box(Wrapper w) {
   158         visitMethodInsn(Opcodes.INVOKESTATIC,
   159                 wrapperName(w),
   160                 NAME_BOX_METHOD,
   161                 boxingDescriptor(w));
   162     }
   163 
   164     /**
   165      * Convert types by unboxing. The source type is known to be a primitive wrapper.
   166      * @param ws A primitive wrapper corresponding to wrapped reference source type
   167      * @param wt A primitive wrapper being converted to
   168      */
   169     void unbox(String sname, Wrapper wt) {
   170         visitMethodInsn(Opcodes.INVOKEVIRTUAL,
   171                 sname,
   172                 unboxMethod(wt),
   173                 unboxingDescriptor(wt));
   174     }
   175 
   176     private String descriptorToName(String desc) {
   177         int last = desc.length() - 1;
   178         if (desc.charAt(0) == 'L' && desc.charAt(last) == ';') {
   179             // In descriptor form
   180             return desc.substring(1, last);
   181         } else {
   182             // Already in internal name form
   183             return desc;
   184         }
   185     }
   186 
   187     void cast(String ds, String dt) {
   188         String ns = descriptorToName(ds);
   189         String nt = descriptorToName(dt);
   190         if (!nt.equals(ns) && !nt.equals(NAME_OBJECT)) {
   191             visitTypeInsn(Opcodes.CHECKCAST, nt);
   192         }
   193     }
   194 
   195     private boolean isPrimitive(Wrapper w) {
   196         return w != OBJECT;
   197     }
   198 
   199     private Wrapper toWrapper(String desc) {
   200         char first = desc.charAt(0);
   201         if (first == '[' || first == '(') {
   202             first = 'L';
   203         }
   204         return Wrapper.forBasicType(first);
   205     }
   206 
   207     /**
   208      * Convert an argument of type 'arg' to be passed to 'target' assuring that it is 'functional'.
   209      * Insert the needed conversion instructions in the method code.
   210      * @param arg
   211      * @param target
   212      * @param functional
   213      */
   214     void convertType(Class<?> arg, Class<?> target, Class<?> functional) {
   215         if (arg.equals(target) && arg.equals(functional)) {
   216             return;
   217         }
   218         if (arg == Void.TYPE || target == Void.TYPE) {
   219             return;
   220         }
   221         if (arg.isPrimitive()) {
   222             Wrapper wArg = Wrapper.forPrimitiveType(arg);
   223             if (target.isPrimitive()) {
   224                 // Both primitives: widening
   225                 widen(wArg, Wrapper.forPrimitiveType(target));
   226             } else {
   227                 // Primitive argument to reference target
   228                 String dTarget = BytecodeDescriptor.unparse(target);
   229                 Wrapper wPrimTarget = wrapperOrNullFromDescriptor(dTarget);
   230                 if (wPrimTarget != null) {
   231                     // The target is a boxed primitive type, widen to get there before boxing
   232                     widen(wArg, wPrimTarget);
   233                     box(wPrimTarget);
   234                 } else {
   235                     // Otherwise, box and cast
   236                     box(wArg);
   237                     cast(wrapperName(wArg), dTarget);
   238                 }
   239             }
   240         } else {
   241             String dArg = BytecodeDescriptor.unparse(arg);
   242             String dSrc;
   243             if (functional.isPrimitive()) {
   244                 dSrc = dArg;
   245             } else {
   246                 // Cast to convert to possibly more specific type, and generate CCE for invalid arg
   247                 dSrc = BytecodeDescriptor.unparse(functional);
   248                 cast(dArg, dSrc);
   249             }
   250             String dTarget = BytecodeDescriptor.unparse(target);
   251             if (target.isPrimitive()) {
   252                 Wrapper wTarget = toWrapper(dTarget);
   253                 // Reference argument to primitive target
   254                 Wrapper wps = wrapperOrNullFromDescriptor(dSrc);
   255                 if (wps != null) {
   256                     if (wps.isSigned() || wps.isFloating()) {
   257                         // Boxed number to primitive
   258                         unbox(wrapperName(wps), wTarget);
   259                     } else {
   260                         // Character or Boolean
   261                         unbox(wrapperName(wps), wps);
   262                         widen(wps, wTarget);
   263                     }
   264                 } else {
   265                     // Source type is reference type, but not boxed type,
   266                     // assume it is super type of target type
   267                     String intermediate;
   268                     if (wTarget.isSigned() || wTarget.isFloating()) {
   269                         // Boxed number to primitive
   270                         intermediate = "java/lang/Number";
   271                     } else {
   272                         // Character or Boolean
   273                         intermediate = wrapperName(wTarget);
   274                     }
   275                     cast(dSrc, intermediate);
   276                     unbox(intermediate, wTarget);
   277                 }
   278             } else {
   279                 // Both reference types: just case to target type
   280                 cast(dSrc, dTarget);
   281             }
   282         }
   283     }
   284 
   285     /**
   286      * The following method is copied from
   287      * org.objectweb.asm.commons.InstructionAdapter. Part of ASM: a very small
   288      * and fast Java bytecode manipulation framework.
   289      * Copyright (c) 2000-2005 INRIA, France Telecom All rights reserved.
   290      */
   291     void iconst(final int cst) {
   292         if (cst >= -1 && cst <= 5) {
   293             mv.visitInsn(Opcodes.ICONST_0 + cst);
   294         } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) {
   295             mv.visitIntInsn(Opcodes.BIPUSH, cst);
   296         } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) {
   297             mv.visitIntInsn(Opcodes.SIPUSH, cst);
   298         } else {
   299             mv.visitLdcInsn(cst);
   300         }
   301     }
   302 }