1 /* 2 * Copyright (c) 2012, 2018, 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 java.lang.constant.ClassDesc; 29 import java.lang.classfile.CodeBuilder; 30 import java.lang.classfile.TypeKind; 31 import java.lang.classfile.constantpool.ConstantPoolBuilder; 32 import java.lang.classfile.constantpool.MethodRefEntry; 33 import jdk.internal.constant.MethodTypeDescImpl; 34 import jdk.internal.constant.ReferenceClassDescImpl; 35 import sun.invoke.util.Wrapper; 36 37 import static java.lang.constant.ConstantDescs.*; 38 39 class TypeConvertingMethodAdapter { 40 41 private static class BoxHolder { 42 private static final ConstantPoolBuilder CP = ConstantPoolBuilder.of(); 43 44 private static MethodRefEntry box(ClassDesc primitive, ClassDesc target) { 45 return CP.methodRefEntry(target, "valueOf", MethodTypeDescImpl.ofValidated(target, primitive)); 46 } 47 48 private static final MethodRefEntry BOX_BOOLEAN = box(CD_boolean, CD_Boolean), 49 BOX_BYTE = box(CD_byte, CD_Byte), 50 BOX_SHORT = box(CD_short, CD_Short), 51 BOX_CHAR = box(CD_char, CD_Character), 52 BOX_INT = box(CD_int, CD_Integer), 53 BOX_LONG = box(CD_long, CD_Long), 54 BOX_FLOAT = box(CD_float, CD_Float), 55 BOX_DOUBLE = box(CD_double, CD_Double); 56 57 private static MethodRefEntry unbox(ClassDesc owner, String methodName, ClassDesc primitiveTarget) { 58 return CP.methodRefEntry(owner, methodName, MethodTypeDescImpl.ofValidated(primitiveTarget)); 59 } 60 61 private static final MethodRefEntry UNBOX_BOOLEAN = unbox(CD_Boolean, "booleanValue", CD_boolean), 62 UNBOX_BYTE = unbox(CD_Number, "byteValue", CD_byte), 63 UNBOX_SHORT = unbox(CD_Number, "shortValue", CD_short), 64 UNBOX_CHAR = unbox(CD_Character, "charValue", CD_char), 65 UNBOX_INT = unbox(CD_Number, "intValue", CD_int), 66 UNBOX_LONG = unbox(CD_Number, "longValue", CD_long), 67 UNBOX_FLOAT = unbox(CD_Number, "floatValue", CD_float), 68 UNBOX_DOUBLE = unbox(CD_Number, "doubleValue", CD_double); 69 } 70 71 private static TypeKind primitiveTypeKindFromClass(Class<?> type) { 72 if (type == Integer.class) return TypeKind.IntType; 73 if (type == Long.class) return TypeKind.LongType; 74 if (type == Boolean.class) return TypeKind.BooleanType; 75 if (type == Short.class) return TypeKind.ShortType; 76 if (type == Byte.class) return TypeKind.ByteType; 77 if (type == Character.class) return TypeKind.CharType; 78 if (type == Float.class) return TypeKind.FloatType; 79 if (type == Double.class) return TypeKind.DoubleType; 80 return null; 81 } 82 83 static void boxIfTypePrimitive(CodeBuilder cob, TypeKind tk) { 84 box(cob, tk); 85 } 86 87 static void widen(CodeBuilder cob, TypeKind ws, TypeKind wt) { 88 ws = ws.asLoadable(); 89 wt = wt.asLoadable(); 90 if (ws != wt) { 91 cob.conversion(ws, wt); 92 } 93 } 94 95 static void box(CodeBuilder cob, TypeKind tk) { 96 switch (tk) { 97 case BooleanType -> cob.invokestatic(BoxHolder.BOX_BOOLEAN); 98 case ByteType -> cob.invokestatic(BoxHolder.BOX_BYTE); 99 case CharType -> cob.invokestatic(BoxHolder.BOX_CHAR); 100 case DoubleType -> cob.invokestatic(BoxHolder.BOX_DOUBLE); 101 case FloatType -> cob.invokestatic(BoxHolder.BOX_FLOAT); 102 case IntType -> cob.invokestatic(BoxHolder.BOX_INT); 103 case LongType -> cob.invokestatic(BoxHolder.BOX_LONG); 104 case ShortType -> cob.invokestatic(BoxHolder.BOX_SHORT); 105 } 106 } 107 108 static void unbox(CodeBuilder cob, TypeKind to) { 109 switch (to) { 110 case BooleanType -> cob.invokevirtual(BoxHolder.UNBOX_BOOLEAN); 111 case ByteType -> cob.invokevirtual(BoxHolder.UNBOX_BYTE); 112 case CharType -> cob.invokevirtual(BoxHolder.UNBOX_CHAR); 113 case DoubleType -> cob.invokevirtual(BoxHolder.UNBOX_DOUBLE); 114 case FloatType -> cob.invokevirtual(BoxHolder.UNBOX_FLOAT); 115 case IntType -> cob.invokevirtual(BoxHolder.UNBOX_INT); 116 case LongType -> cob.invokevirtual(BoxHolder.UNBOX_LONG); 117 case ShortType -> cob.invokevirtual(BoxHolder.UNBOX_SHORT); 118 } 119 } 120 121 static void cast(CodeBuilder cob, ClassDesc dt) { 122 if (!dt.equals(CD_Object)) { 123 cob.checkcast(dt); 124 } 125 } 126 127 /** 128 * Convert an argument of type 'arg' to be passed to 'target' assuring that it is 'functional'. 129 * Insert the needed conversion instructions in the method code. 130 * @param arg 131 * @param target 132 * @param functional 133 */ 134 static void convertType(CodeBuilder cob, Class<?> arg, Class<?> target, Class<?> functional) { 135 if (arg.equals(target) && arg.equals(functional)) { 136 return; 137 } 138 if (arg == Void.TYPE || target == Void.TYPE) { 139 return; 140 } 141 if (arg.isPrimitive()) { 142 if (target.isPrimitive()) { 143 // Both primitives: widening 144 widen(cob, TypeKind.from(arg), TypeKind.from(target)); 145 } else { 146 // Primitive argument to reference target 147 TypeKind wPrimTk = primitiveTypeKindFromClass(target); 148 if (wPrimTk != null) { 149 // The target is a boxed primitive type, widen to get there before boxing 150 widen(cob, TypeKind.from(arg), wPrimTk); 151 box(cob, wPrimTk); 152 } else { 153 // Otherwise, box and cast 154 box(cob, TypeKind.from(arg)); 155 cast(cob, classDesc(target)); 156 } 157 } 158 } else { 159 Class<?> src; 160 if (arg == functional || functional.isPrimitive()) { 161 src = arg; 162 } else { 163 // Cast to convert to possibly more specific type, and generate CCE for invalid arg 164 src = functional; 165 cast(cob, classDesc(functional)); 166 } 167 if (target.isPrimitive()) { 168 // Reference argument to primitive target 169 TypeKind wps = primitiveTypeKindFromClass(src); 170 if (wps != null) { 171 if (src != Character.class && src != Boolean.class) { 172 // Boxed number to primitive 173 unbox(cob, TypeKind.from(target)); 174 } else { 175 // Character or Boolean 176 unbox(cob, wps); 177 widen(cob, wps, TypeKind.from(target)); 178 } 179 } else { 180 // Source type is reference type, but not boxed type, 181 // assume it is super type of target type 182 if (target == char.class) { 183 cast(cob, CD_Character); 184 } else if (target == boolean.class) { 185 cast(cob, CD_Boolean); 186 } else { 187 // Boxed number to primitive 188 cast(cob, CD_Number); 189 } 190 unbox(cob, TypeKind.from(target)); 191 } 192 } else { 193 // Both reference types: just case to target type 194 if (src != target) { 195 cast(cob, classDesc(target)); 196 } 197 } 198 } 199 } 200 201 static ClassDesc classDesc(Class<?> cls) { 202 return cls.isPrimitive() ? Wrapper.forPrimitiveType(cls).basicClassDescriptor() 203 : cls == Object.class ? CD_Object 204 : cls == String.class ? CD_String 205 : ReferenceClassDescImpl.ofValidated(cls.descriptorString()); 206 } 207 }