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.ASM5, 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 }