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 == int.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 == char.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 }