1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/rt/emul/compact/src/main/java/java/lang/invoke/TypeConvertingMethodAdapter.java Sat Aug 09 11:11:13 2014 +0200
1.3 @@ -0,0 +1,302 @@
1.4 +/*
1.5 + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
1.7 + *
1.8 + * This code is free software; you can redistribute it and/or modify it
1.9 + * under the terms of the GNU General Public License version 2 only, as
1.10 + * published by the Free Software Foundation. Oracle designates this
1.11 + * particular file as subject to the "Classpath" exception as provided
1.12 + * by Oracle in the LICENSE file that accompanied this code.
1.13 + *
1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
1.17 + * version 2 for more details (a copy is included in the LICENSE file that
1.18 + * accompanied this code).
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License version
1.21 + * 2 along with this work; if not, write to the Free Software Foundation,
1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1.23 + *
1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
1.25 + * or visit www.oracle.com if you need additional information or have any
1.26 + * questions.
1.27 + */
1.28 +
1.29 +package java.lang.invoke;
1.30 +
1.31 +import jdk.internal.org.objectweb.asm.MethodVisitor;
1.32 +import jdk.internal.org.objectweb.asm.Opcodes;
1.33 +import jdk.internal.org.objectweb.asm.Type;
1.34 +import sun.invoke.util.BytecodeDescriptor;
1.35 +import sun.invoke.util.Wrapper;
1.36 +import static sun.invoke.util.Wrapper.*;
1.37 +
1.38 +class TypeConvertingMethodAdapter extends MethodVisitor {
1.39 +
1.40 + TypeConvertingMethodAdapter(MethodVisitor mv) {
1.41 + super(Opcodes.ASM5, mv);
1.42 + }
1.43 +
1.44 + private static final int NUM_WRAPPERS = Wrapper.values().length;
1.45 +
1.46 + private static final String NAME_OBJECT = "java/lang/Object";
1.47 + private static final String WRAPPER_PREFIX = "Ljava/lang/";
1.48 +
1.49 + // Same for all primitives; name of the boxing method
1.50 + private static final String NAME_BOX_METHOD = "valueOf";
1.51 +
1.52 + // Table of opcodes for widening primitive conversions; NOP = no conversion
1.53 + private static final int[][] wideningOpcodes = new int[NUM_WRAPPERS][NUM_WRAPPERS];
1.54 +
1.55 + private static final Wrapper[] FROM_WRAPPER_NAME = new Wrapper[16];
1.56 +
1.57 + // Table of wrappers for primitives, indexed by ASM type sorts
1.58 + private static final Wrapper[] FROM_TYPE_SORT = new Wrapper[16];
1.59 +
1.60 + static {
1.61 + for (Wrapper w : Wrapper.values()) {
1.62 + if (w.basicTypeChar() != 'L') {
1.63 + int wi = hashWrapperName(w.wrapperSimpleName());
1.64 + assert (FROM_WRAPPER_NAME[wi] == null);
1.65 + FROM_WRAPPER_NAME[wi] = w;
1.66 + }
1.67 + }
1.68 +
1.69 + for (int i = 0; i < NUM_WRAPPERS; i++) {
1.70 + for (int j = 0; j < NUM_WRAPPERS; j++) {
1.71 + wideningOpcodes[i][j] = Opcodes.NOP;
1.72 + }
1.73 + }
1.74 +
1.75 + initWidening(LONG, Opcodes.I2L, BYTE, SHORT, INT, CHAR);
1.76 + initWidening(LONG, Opcodes.F2L, FLOAT);
1.77 + initWidening(FLOAT, Opcodes.I2F, BYTE, SHORT, INT, CHAR);
1.78 + initWidening(FLOAT, Opcodes.L2F, LONG);
1.79 + initWidening(DOUBLE, Opcodes.I2D, BYTE, SHORT, INT, CHAR);
1.80 + initWidening(DOUBLE, Opcodes.F2D, FLOAT);
1.81 + initWidening(DOUBLE, Opcodes.L2D, LONG);
1.82 +
1.83 + FROM_TYPE_SORT[Type.BYTE] = Wrapper.BYTE;
1.84 + FROM_TYPE_SORT[Type.SHORT] = Wrapper.SHORT;
1.85 + FROM_TYPE_SORT[Type.INT] = Wrapper.INT;
1.86 + FROM_TYPE_SORT[Type.LONG] = Wrapper.LONG;
1.87 + FROM_TYPE_SORT[Type.CHAR] = Wrapper.CHAR;
1.88 + FROM_TYPE_SORT[Type.FLOAT] = Wrapper.FLOAT;
1.89 + FROM_TYPE_SORT[Type.DOUBLE] = Wrapper.DOUBLE;
1.90 + FROM_TYPE_SORT[Type.BOOLEAN] = Wrapper.BOOLEAN;
1.91 + }
1.92 +
1.93 + private static void initWidening(Wrapper to, int opcode, Wrapper... from) {
1.94 + for (Wrapper f : from) {
1.95 + wideningOpcodes[f.ordinal()][to.ordinal()] = opcode;
1.96 + }
1.97 + }
1.98 +
1.99 + /**
1.100 + * Class name to Wrapper hash, derived from Wrapper.hashWrap()
1.101 + * @param xn
1.102 + * @return The hash code 0-15
1.103 + */
1.104 + private static int hashWrapperName(String xn) {
1.105 + if (xn.length() < 3) {
1.106 + return 0;
1.107 + }
1.108 + return (3 * xn.charAt(1) + xn.charAt(2)) % 16;
1.109 + }
1.110 +
1.111 + private Wrapper wrapperOrNullFromDescriptor(String desc) {
1.112 + if (!desc.startsWith(WRAPPER_PREFIX)) {
1.113 + // Not a class type (array or method), so not a boxed type
1.114 + // or not in the right package
1.115 + return null;
1.116 + }
1.117 + // Pare it down to the simple class name
1.118 + String cname = desc.substring(WRAPPER_PREFIX.length(), desc.length() - 1);
1.119 + // Hash to a Wrapper
1.120 + Wrapper w = FROM_WRAPPER_NAME[hashWrapperName(cname)];
1.121 + if (w == null || w.wrapperSimpleName().equals(cname)) {
1.122 + return w;
1.123 + } else {
1.124 + return null;
1.125 + }
1.126 + }
1.127 +
1.128 + private static String wrapperName(Wrapper w) {
1.129 + return "java/lang/" + w.wrapperSimpleName();
1.130 + }
1.131 +
1.132 + private static String unboxMethod(Wrapper w) {
1.133 + return w.primitiveSimpleName() + "Value";
1.134 + }
1.135 +
1.136 + private static String boxingDescriptor(Wrapper w) {
1.137 + return String.format("(%s)L%s;", w.basicTypeChar(), wrapperName(w));
1.138 + }
1.139 +
1.140 + private static String unboxingDescriptor(Wrapper w) {
1.141 + return "()" + w.basicTypeChar();
1.142 + }
1.143 +
1.144 + void boxIfTypePrimitive(Type t) {
1.145 + Wrapper w = FROM_TYPE_SORT[t.getSort()];
1.146 + if (w != null) {
1.147 + box(w);
1.148 + }
1.149 + }
1.150 +
1.151 + void widen(Wrapper ws, Wrapper wt) {
1.152 + if (ws != wt) {
1.153 + int opcode = wideningOpcodes[ws.ordinal()][wt.ordinal()];
1.154 + if (opcode != Opcodes.NOP) {
1.155 + visitInsn(opcode);
1.156 + }
1.157 + }
1.158 + }
1.159 +
1.160 + void box(Wrapper w) {
1.161 + visitMethodInsn(Opcodes.INVOKESTATIC,
1.162 + wrapperName(w),
1.163 + NAME_BOX_METHOD,
1.164 + boxingDescriptor(w));
1.165 + }
1.166 +
1.167 + /**
1.168 + * Convert types by unboxing. The source type is known to be a primitive wrapper.
1.169 + * @param ws A primitive wrapper corresponding to wrapped reference source type
1.170 + * @param wt A primitive wrapper being converted to
1.171 + */
1.172 + void unbox(String sname, Wrapper wt) {
1.173 + visitMethodInsn(Opcodes.INVOKEVIRTUAL,
1.174 + sname,
1.175 + unboxMethod(wt),
1.176 + unboxingDescriptor(wt));
1.177 + }
1.178 +
1.179 + private String descriptorToName(String desc) {
1.180 + int last = desc.length() - 1;
1.181 + if (desc.charAt(0) == 'L' && desc.charAt(last) == ';') {
1.182 + // In descriptor form
1.183 + return desc.substring(1, last);
1.184 + } else {
1.185 + // Already in internal name form
1.186 + return desc;
1.187 + }
1.188 + }
1.189 +
1.190 + void cast(String ds, String dt) {
1.191 + String ns = descriptorToName(ds);
1.192 + String nt = descriptorToName(dt);
1.193 + if (!nt.equals(ns) && !nt.equals(NAME_OBJECT)) {
1.194 + visitTypeInsn(Opcodes.CHECKCAST, nt);
1.195 + }
1.196 + }
1.197 +
1.198 + private boolean isPrimitive(Wrapper w) {
1.199 + return w != OBJECT;
1.200 + }
1.201 +
1.202 + private Wrapper toWrapper(String desc) {
1.203 + char first = desc.charAt(0);
1.204 + if (first == '[' || first == '(') {
1.205 + first = 'L';
1.206 + }
1.207 + return Wrapper.forBasicType(first);
1.208 + }
1.209 +
1.210 + /**
1.211 + * Convert an argument of type 'arg' to be passed to 'target' assuring that it is 'functional'.
1.212 + * Insert the needed conversion instructions in the method code.
1.213 + * @param arg
1.214 + * @param target
1.215 + * @param functional
1.216 + */
1.217 + void convertType(Class<?> arg, Class<?> target, Class<?> functional) {
1.218 + if (arg.equals(target) && arg.equals(functional)) {
1.219 + return;
1.220 + }
1.221 + if (arg == Void.TYPE || target == Void.TYPE) {
1.222 + return;
1.223 + }
1.224 + if (arg.isPrimitive()) {
1.225 + Wrapper wArg = Wrapper.forPrimitiveType(arg);
1.226 + if (target.isPrimitive()) {
1.227 + // Both primitives: widening
1.228 + widen(wArg, Wrapper.forPrimitiveType(target));
1.229 + } else {
1.230 + // Primitive argument to reference target
1.231 + String dTarget = BytecodeDescriptor.unparse(target);
1.232 + Wrapper wPrimTarget = wrapperOrNullFromDescriptor(dTarget);
1.233 + if (wPrimTarget != null) {
1.234 + // The target is a boxed primitive type, widen to get there before boxing
1.235 + widen(wArg, wPrimTarget);
1.236 + box(wPrimTarget);
1.237 + } else {
1.238 + // Otherwise, box and cast
1.239 + box(wArg);
1.240 + cast(wrapperName(wArg), dTarget);
1.241 + }
1.242 + }
1.243 + } else {
1.244 + String dArg = BytecodeDescriptor.unparse(arg);
1.245 + String dSrc;
1.246 + if (functional.isPrimitive()) {
1.247 + dSrc = dArg;
1.248 + } else {
1.249 + // Cast to convert to possibly more specific type, and generate CCE for invalid arg
1.250 + dSrc = BytecodeDescriptor.unparse(functional);
1.251 + cast(dArg, dSrc);
1.252 + }
1.253 + String dTarget = BytecodeDescriptor.unparse(target);
1.254 + if (target.isPrimitive()) {
1.255 + Wrapper wTarget = toWrapper(dTarget);
1.256 + // Reference argument to primitive target
1.257 + Wrapper wps = wrapperOrNullFromDescriptor(dSrc);
1.258 + if (wps != null) {
1.259 + if (wps.isSigned() || wps.isFloating()) {
1.260 + // Boxed number to primitive
1.261 + unbox(wrapperName(wps), wTarget);
1.262 + } else {
1.263 + // Character or Boolean
1.264 + unbox(wrapperName(wps), wps);
1.265 + widen(wps, wTarget);
1.266 + }
1.267 + } else {
1.268 + // Source type is reference type, but not boxed type,
1.269 + // assume it is super type of target type
1.270 + String intermediate;
1.271 + if (wTarget.isSigned() || wTarget.isFloating()) {
1.272 + // Boxed number to primitive
1.273 + intermediate = "java/lang/Number";
1.274 + } else {
1.275 + // Character or Boolean
1.276 + intermediate = wrapperName(wTarget);
1.277 + }
1.278 + cast(dSrc, intermediate);
1.279 + unbox(intermediate, wTarget);
1.280 + }
1.281 + } else {
1.282 + // Both reference types: just case to target type
1.283 + cast(dSrc, dTarget);
1.284 + }
1.285 + }
1.286 + }
1.287 +
1.288 + /**
1.289 + * The following method is copied from
1.290 + * org.objectweb.asm.commons.InstructionAdapter. Part of ASM: a very small
1.291 + * and fast Java bytecode manipulation framework.
1.292 + * Copyright (c) 2000-2005 INRIA, France Telecom All rights reserved.
1.293 + */
1.294 + void iconst(final int cst) {
1.295 + if (cst >= -1 && cst <= 5) {
1.296 + mv.visitInsn(Opcodes.ICONST_0 + cst);
1.297 + } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) {
1.298 + mv.visitIntInsn(Opcodes.BIPUSH, cst);
1.299 + } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) {
1.300 + mv.visitIntInsn(Opcodes.SIPUSH, cst);
1.301 + } else {
1.302 + mv.visitLdcInsn(cst);
1.303 + }
1.304 + }
1.305 +}