jaroslav@1646: /* jaroslav@1646: * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. jaroslav@1646: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jaroslav@1646: * jaroslav@1646: * This code is free software; you can redistribute it and/or modify it jaroslav@1646: * under the terms of the GNU General Public License version 2 only, as jaroslav@1646: * published by the Free Software Foundation. Oracle designates this jaroslav@1646: * particular file as subject to the "Classpath" exception as provided jaroslav@1646: * by Oracle in the LICENSE file that accompanied this code. jaroslav@1646: * jaroslav@1646: * This code is distributed in the hope that it will be useful, but WITHOUT jaroslav@1646: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jaroslav@1646: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jaroslav@1646: * version 2 for more details (a copy is included in the LICENSE file that jaroslav@1646: * accompanied this code). jaroslav@1646: * jaroslav@1646: * You should have received a copy of the GNU General Public License version jaroslav@1646: * 2 along with this work; if not, write to the Free Software Foundation, jaroslav@1646: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jaroslav@1646: * jaroslav@1646: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA jaroslav@1646: * or visit www.oracle.com if you need additional information or have any jaroslav@1646: * questions. jaroslav@1646: */ jaroslav@1646: jaroslav@1646: package java.lang.invoke; jaroslav@1646: jaroslav@1646: import jdk.internal.org.objectweb.asm.MethodVisitor; jaroslav@1646: import jdk.internal.org.objectweb.asm.Opcodes; jaroslav@1646: import jdk.internal.org.objectweb.asm.Type; jaroslav@1646: import sun.invoke.util.BytecodeDescriptor; jaroslav@1646: import sun.invoke.util.Wrapper; jaroslav@1646: import static sun.invoke.util.Wrapper.*; jaroslav@1646: jaroslav@1646: class TypeConvertingMethodAdapter extends MethodVisitor { jaroslav@1646: jaroslav@1646: TypeConvertingMethodAdapter(MethodVisitor mv) { jaroslav@1646: super(Opcodes.ASM5, mv); jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static final int NUM_WRAPPERS = Wrapper.values().length; jaroslav@1646: jaroslav@1646: private static final String NAME_OBJECT = "java/lang/Object"; jaroslav@1646: private static final String WRAPPER_PREFIX = "Ljava/lang/"; jaroslav@1646: jaroslav@1646: // Same for all primitives; name of the boxing method jaroslav@1646: private static final String NAME_BOX_METHOD = "valueOf"; jaroslav@1646: jaroslav@1646: // Table of opcodes for widening primitive conversions; NOP = no conversion jaroslav@1646: private static final int[][] wideningOpcodes = new int[NUM_WRAPPERS][NUM_WRAPPERS]; jaroslav@1646: jaroslav@1646: private static final Wrapper[] FROM_WRAPPER_NAME = new Wrapper[16]; jaroslav@1646: jaroslav@1646: // Table of wrappers for primitives, indexed by ASM type sorts jaroslav@1646: private static final Wrapper[] FROM_TYPE_SORT = new Wrapper[16]; jaroslav@1646: jaroslav@1646: static { jaroslav@1646: for (Wrapper w : Wrapper.values()) { jaroslav@1646: if (w.basicTypeChar() != 'L') { jaroslav@1646: int wi = hashWrapperName(w.wrapperSimpleName()); jaroslav@1646: assert (FROM_WRAPPER_NAME[wi] == null); jaroslav@1646: FROM_WRAPPER_NAME[wi] = w; jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: for (int i = 0; i < NUM_WRAPPERS; i++) { jaroslav@1646: for (int j = 0; j < NUM_WRAPPERS; j++) { jaroslav@1646: wideningOpcodes[i][j] = Opcodes.NOP; jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: initWidening(LONG, Opcodes.I2L, BYTE, SHORT, INT, CHAR); jaroslav@1646: initWidening(LONG, Opcodes.F2L, FLOAT); jaroslav@1646: initWidening(FLOAT, Opcodes.I2F, BYTE, SHORT, INT, CHAR); jaroslav@1646: initWidening(FLOAT, Opcodes.L2F, LONG); jaroslav@1646: initWidening(DOUBLE, Opcodes.I2D, BYTE, SHORT, INT, CHAR); jaroslav@1646: initWidening(DOUBLE, Opcodes.F2D, FLOAT); jaroslav@1646: initWidening(DOUBLE, Opcodes.L2D, LONG); jaroslav@1646: jaroslav@1646: FROM_TYPE_SORT[Type.BYTE] = Wrapper.BYTE; jaroslav@1646: FROM_TYPE_SORT[Type.SHORT] = Wrapper.SHORT; jaroslav@1646: FROM_TYPE_SORT[Type.INT] = Wrapper.INT; jaroslav@1646: FROM_TYPE_SORT[Type.LONG] = Wrapper.LONG; jaroslav@1646: FROM_TYPE_SORT[Type.CHAR] = Wrapper.CHAR; jaroslav@1646: FROM_TYPE_SORT[Type.FLOAT] = Wrapper.FLOAT; jaroslav@1646: FROM_TYPE_SORT[Type.DOUBLE] = Wrapper.DOUBLE; jaroslav@1646: FROM_TYPE_SORT[Type.BOOLEAN] = Wrapper.BOOLEAN; jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static void initWidening(Wrapper to, int opcode, Wrapper... from) { jaroslav@1646: for (Wrapper f : from) { jaroslav@1646: wideningOpcodes[f.ordinal()][to.ordinal()] = opcode; jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: /** jaroslav@1646: * Class name to Wrapper hash, derived from Wrapper.hashWrap() jaroslav@1646: * @param xn jaroslav@1646: * @return The hash code 0-15 jaroslav@1646: */ jaroslav@1646: private static int hashWrapperName(String xn) { jaroslav@1646: if (xn.length() < 3) { jaroslav@1646: return 0; jaroslav@1646: } jaroslav@1646: return (3 * xn.charAt(1) + xn.charAt(2)) % 16; jaroslav@1646: } jaroslav@1646: jaroslav@1646: private Wrapper wrapperOrNullFromDescriptor(String desc) { jaroslav@1646: if (!desc.startsWith(WRAPPER_PREFIX)) { jaroslav@1646: // Not a class type (array or method), so not a boxed type jaroslav@1646: // or not in the right package jaroslav@1646: return null; jaroslav@1646: } jaroslav@1646: // Pare it down to the simple class name jaroslav@1646: String cname = desc.substring(WRAPPER_PREFIX.length(), desc.length() - 1); jaroslav@1646: // Hash to a Wrapper jaroslav@1646: Wrapper w = FROM_WRAPPER_NAME[hashWrapperName(cname)]; jaroslav@1646: if (w == null || w.wrapperSimpleName().equals(cname)) { jaroslav@1646: return w; jaroslav@1646: } else { jaroslav@1646: return null; jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static String wrapperName(Wrapper w) { jaroslav@1646: return "java/lang/" + w.wrapperSimpleName(); jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static String unboxMethod(Wrapper w) { jaroslav@1646: return w.primitiveSimpleName() + "Value"; jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static String boxingDescriptor(Wrapper w) { jaroslav@1646: return String.format("(%s)L%s;", w.basicTypeChar(), wrapperName(w)); jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static String unboxingDescriptor(Wrapper w) { jaroslav@1646: return "()" + w.basicTypeChar(); jaroslav@1646: } jaroslav@1646: jaroslav@1646: void boxIfTypePrimitive(Type t) { jaroslav@1646: Wrapper w = FROM_TYPE_SORT[t.getSort()]; jaroslav@1646: if (w != null) { jaroslav@1646: box(w); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: void widen(Wrapper ws, Wrapper wt) { jaroslav@1646: if (ws != wt) { jaroslav@1646: int opcode = wideningOpcodes[ws.ordinal()][wt.ordinal()]; jaroslav@1646: if (opcode != Opcodes.NOP) { jaroslav@1646: visitInsn(opcode); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: void box(Wrapper w) { jaroslav@1646: visitMethodInsn(Opcodes.INVOKESTATIC, jaroslav@1646: wrapperName(w), jaroslav@1646: NAME_BOX_METHOD, jaroslav@1646: boxingDescriptor(w)); jaroslav@1646: } jaroslav@1646: jaroslav@1646: /** jaroslav@1646: * Convert types by unboxing. The source type is known to be a primitive wrapper. jaroslav@1646: * @param ws A primitive wrapper corresponding to wrapped reference source type jaroslav@1646: * @param wt A primitive wrapper being converted to jaroslav@1646: */ jaroslav@1646: void unbox(String sname, Wrapper wt) { jaroslav@1646: visitMethodInsn(Opcodes.INVOKEVIRTUAL, jaroslav@1646: sname, jaroslav@1646: unboxMethod(wt), jaroslav@1646: unboxingDescriptor(wt)); jaroslav@1646: } jaroslav@1646: jaroslav@1646: private String descriptorToName(String desc) { jaroslav@1646: int last = desc.length() - 1; jaroslav@1646: if (desc.charAt(0) == 'L' && desc.charAt(last) == ';') { jaroslav@1646: // In descriptor form jaroslav@1646: return desc.substring(1, last); jaroslav@1646: } else { jaroslav@1646: // Already in internal name form jaroslav@1646: return desc; jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: void cast(String ds, String dt) { jaroslav@1646: String ns = descriptorToName(ds); jaroslav@1646: String nt = descriptorToName(dt); jaroslav@1646: if (!nt.equals(ns) && !nt.equals(NAME_OBJECT)) { jaroslav@1646: visitTypeInsn(Opcodes.CHECKCAST, nt); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: private boolean isPrimitive(Wrapper w) { jaroslav@1646: return w != OBJECT; jaroslav@1646: } jaroslav@1646: jaroslav@1646: private Wrapper toWrapper(String desc) { jaroslav@1646: char first = desc.charAt(0); jaroslav@1646: if (first == '[' || first == '(') { jaroslav@1646: first = 'L'; jaroslav@1646: } jaroslav@1646: return Wrapper.forBasicType(first); jaroslav@1646: } jaroslav@1646: jaroslav@1646: /** jaroslav@1646: * Convert an argument of type 'arg' to be passed to 'target' assuring that it is 'functional'. jaroslav@1646: * Insert the needed conversion instructions in the method code. jaroslav@1646: * @param arg jaroslav@1646: * @param target jaroslav@1646: * @param functional jaroslav@1646: */ jaroslav@1646: void convertType(Class arg, Class target, Class functional) { jaroslav@1646: if (arg.equals(target) && arg.equals(functional)) { jaroslav@1646: return; jaroslav@1646: } jaroslav@1646: if (arg == Void.TYPE || target == Void.TYPE) { jaroslav@1646: return; jaroslav@1646: } jaroslav@1646: if (arg.isPrimitive()) { jaroslav@1646: Wrapper wArg = Wrapper.forPrimitiveType(arg); jaroslav@1646: if (target.isPrimitive()) { jaroslav@1646: // Both primitives: widening jaroslav@1646: widen(wArg, Wrapper.forPrimitiveType(target)); jaroslav@1646: } else { jaroslav@1646: // Primitive argument to reference target jaroslav@1646: String dTarget = BytecodeDescriptor.unparse(target); jaroslav@1646: Wrapper wPrimTarget = wrapperOrNullFromDescriptor(dTarget); jaroslav@1646: if (wPrimTarget != null) { jaroslav@1646: // The target is a boxed primitive type, widen to get there before boxing jaroslav@1646: widen(wArg, wPrimTarget); jaroslav@1646: box(wPrimTarget); jaroslav@1646: } else { jaroslav@1646: // Otherwise, box and cast jaroslav@1646: box(wArg); jaroslav@1646: cast(wrapperName(wArg), dTarget); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: } else { jaroslav@1646: String dArg = BytecodeDescriptor.unparse(arg); jaroslav@1646: String dSrc; jaroslav@1646: if (functional.isPrimitive()) { jaroslav@1646: dSrc = dArg; jaroslav@1646: } else { jaroslav@1646: // Cast to convert to possibly more specific type, and generate CCE for invalid arg jaroslav@1646: dSrc = BytecodeDescriptor.unparse(functional); jaroslav@1646: cast(dArg, dSrc); jaroslav@1646: } jaroslav@1646: String dTarget = BytecodeDescriptor.unparse(target); jaroslav@1646: if (target.isPrimitive()) { jaroslav@1646: Wrapper wTarget = toWrapper(dTarget); jaroslav@1646: // Reference argument to primitive target jaroslav@1646: Wrapper wps = wrapperOrNullFromDescriptor(dSrc); jaroslav@1646: if (wps != null) { jaroslav@1646: if (wps.isSigned() || wps.isFloating()) { jaroslav@1646: // Boxed number to primitive jaroslav@1646: unbox(wrapperName(wps), wTarget); jaroslav@1646: } else { jaroslav@1646: // Character or Boolean jaroslav@1646: unbox(wrapperName(wps), wps); jaroslav@1646: widen(wps, wTarget); jaroslav@1646: } jaroslav@1646: } else { jaroslav@1646: // Source type is reference type, but not boxed type, jaroslav@1646: // assume it is super type of target type jaroslav@1646: String intermediate; jaroslav@1646: if (wTarget.isSigned() || wTarget.isFloating()) { jaroslav@1646: // Boxed number to primitive jaroslav@1646: intermediate = "java/lang/Number"; jaroslav@1646: } else { jaroslav@1646: // Character or Boolean jaroslav@1646: intermediate = wrapperName(wTarget); jaroslav@1646: } jaroslav@1646: cast(dSrc, intermediate); jaroslav@1646: unbox(intermediate, wTarget); jaroslav@1646: } jaroslav@1646: } else { jaroslav@1646: // Both reference types: just case to target type jaroslav@1646: cast(dSrc, dTarget); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: /** jaroslav@1646: * The following method is copied from jaroslav@1646: * org.objectweb.asm.commons.InstructionAdapter. Part of ASM: a very small jaroslav@1646: * and fast Java bytecode manipulation framework. jaroslav@1646: * Copyright (c) 2000-2005 INRIA, France Telecom All rights reserved. jaroslav@1646: */ jaroslav@1646: void iconst(final int cst) { jaroslav@1646: if (cst >= -1 && cst <= 5) { jaroslav@1646: mv.visitInsn(Opcodes.ICONST_0 + cst); jaroslav@1646: } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) { jaroslav@1646: mv.visitIntInsn(Opcodes.BIPUSH, cst); jaroslav@1646: } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) { jaroslav@1646: mv.visitIntInsn(Opcodes.SIPUSH, cst); jaroslav@1646: } else { jaroslav@1646: mv.visitLdcInsn(cst); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: }