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