/* * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the Classpath exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.btrace.runtime; import com.sun.btrace.AnyType; import static com.sun.btrace.runtime.Constants.*; import static com.sun.btrace.org.objectweb.asm.Opcodes.*; import java.util.Map; import java.util.HashMap; import java.util.StringTokenizer; import com.sun.btrace.org.objectweb.asm.Type; public final class TypeUtils { private TypeUtils() {} public static final Type objectArrayType = Type.getType(Object[].class); public static final Type anyTypeArray = Type.getType(AnyType[].class); public static boolean isPrimitive(String typeDesc) { if (typeDesc.length() == 1) { switch (typeDesc.charAt(0)) { case 'I': case 'J': case 'F': case 'D': case 'Z': case 'C': case 'B': { return true; } } } return false; } @SuppressWarnings("ReferenceEquality") public static boolean isPrimitive(Type t) { return t == Type.BOOLEAN_TYPE || t == Type.BYTE_TYPE || t == Type.CHAR_TYPE || t == Type.DOUBLE_TYPE || t == Type.FLOAT_TYPE || t == Type.INT_TYPE || t == Type.LONG_TYPE || t == Type.SHORT_TYPE; } public static boolean isAnyType(Type t) { return t.equals(ANYTYPE_TYPE); } public static boolean isAnyTypeArray(Type t) { return t.equals(anyTypeArray); } public static boolean isObject(Type t) { return t.equals(OBJECT_TYPE); } public static boolean isObjectOrAnyType(Type t) { return isObject(t) || isAnyType(t); } public static boolean isString(Type t) { return t.equals(STRING_TYPE); } public static boolean isArray(Type t) { return t.getSort() == Type.ARRAY; } public static boolean isThrowable(Type t) { return t.equals(THROWABLE_TYPE); } public static boolean isVoid(Type t) { return Type.VOID_TYPE.equals(t) || VOIDREF_TYPE.equals(t); } /** * Checks the type compatibility * <p> * Two types are compatible when and only when: * <ol> * <li>They are exactly the same</li> * <li>The left parameter is {@linkplain Object} or {@linkplain AnyType} and * the right parameter is either {@linkplain Object} or an array</li> * <li>The left parameter is assignable from the right one (is a superclass of the right one)</li> * </ol> * </p> * @param left The left type parameter * @param right The right type parameter * @return Returns true if a value of the left {@linkplain Type} can hold the value of the right {@linkplain Type} */ public static boolean isCompatible(Type left, Type right) { if (left.equals(right)) { return true; } else if (isVoid(left)) { return isVoid(right); } else if (isArray(left)) { return false; } else if(isObjectOrAnyType(left)) { int sort2 = right.getSort(); return (sort2 == Type.OBJECT || sort2 == Type.ARRAY || sort2 == Type.VOID || isPrimitive(right)); } else if (isPrimitive(left)) { // a primitive type requires strict equality return left.equals(right); } else { ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl == null) { cl = ClassLoader.getSystemClassLoader(); } // those classes should already have been loaded at this point Class clzLeft, clzRight; try { clzLeft = cl.loadClass(left.getClassName()); } catch (Throwable e) { clzLeft = Object.class; } // anything is assignable to Object if (clzLeft == Object.class) { return true; } try { clzRight = cl.loadClass(right.getClassName()); } catch (Throwable e) { clzRight = Object.class; } return (clzLeft.isAssignableFrom(clzRight)); } } public static boolean isCompatible(Type[] args1, Type[] args2) { if (args1.length != args2.length) { return false; } for (int i = 0; i < args1.length; i++) { if (! args1[i].equals(args2[i])) { int sort2 = args2[i].getSort(); /* * if destination is AnyType and right side is * Object or Array (i.e., any reference type) * then we allow it - because AnyType is mapped to * java.lang.Object. */ if (isAnyType(args1[i]) && (sort2 == Type.OBJECT || sort2 == Type.ARRAY || isPrimitive(args2[i]))) { } else { return false; } } } return true; } public static Type getArrayType(int arrayOpcode) { switch (arrayOpcode) { case IALOAD: case IASTORE: return Type.getType("[I"); case BALOAD: case BASTORE: return Type.getType("[B"); case AALOAD: case AASTORE: return objectArrayType; case CALOAD: case CASTORE: return Type.getType("[C"); case FALOAD: case FASTORE: return Type.getType("[F"); case SALOAD: case SASTORE: return Type.getType("[S"); case LALOAD: case LASTORE: return Type.getType("[J"); case DALOAD: case DASTORE: return Type.getType("[D"); default: throw new RuntimeException("invalid array opcode"); } } public static Type getElementType(int arrayOpcode) { switch (arrayOpcode) { case IALOAD: case IASTORE: return Type.INT_TYPE; case BALOAD: case BASTORE: return Type.BYTE_TYPE; case AALOAD: case AASTORE: return OBJECT_TYPE; case CALOAD: case CASTORE: return Type.CHAR_TYPE; case FALOAD: case FASTORE: return Type.FLOAT_TYPE; case SALOAD: case SASTORE: return Type.SHORT_TYPE; case LALOAD: case LASTORE: return Type.LONG_TYPE; case DALOAD: case DASTORE: return Type.DOUBLE_TYPE; default: throw new RuntimeException("invalid array opcode"); } } private static final Map<String, String> primitives; static { primitives = new HashMap<>(); primitives.put("void", "V"); primitives.put("byte", "B"); primitives.put("char", "C"); primitives.put("double", "D"); primitives.put("float", "F"); primitives.put("int", "I"); primitives.put("long", "J"); primitives.put("short", "S"); primitives.put("boolean", "Z"); } public static String declarationToDescriptor(String decl) { int leftParen = decl.indexOf('('); int rightParen = decl.indexOf(')'); if (leftParen == -1 || rightParen == -1) { throw new IllegalArgumentException(); } StringBuilder buf = new StringBuilder(); String descriptor; buf.append('('); String args = decl.substring(leftParen + 1, rightParen); StringTokenizer st = new StringTokenizer(args, ","); while (st.hasMoreTokens()) { String arg = st.nextToken().trim(); descriptor = primitives.get(arg); if (arg.length() == 0) { throw new IllegalArgumentException(); } if (descriptor == null) { descriptor = objectOrArrayType(arg); } buf.append(descriptor); } buf.append(')'); String returnType = decl.substring(0, leftParen).trim(); descriptor = primitives.get(returnType); if (returnType.length() == 0) { throw new IllegalArgumentException(); } if (descriptor == null) { descriptor = objectOrArrayType(returnType); } buf.append(descriptor); return buf.toString(); } public static String descriptorToSimplified(String desc, String owner, String name) { String retTypeDesc = null; Type[] args = null; if (desc.contains("(")) { retTypeDesc = Type.getReturnType(desc).getDescriptor(); args = Type.getArgumentTypes(desc); } else { retTypeDesc = isPrimitive(desc) ? desc : Type.getType(desc).getDescriptor(); args = new Type[0]; } StringBuilder sb = new StringBuilder(); sb.append(getJavaType(retTypeDesc)).append(' ') .append(owner.replace('/', '.')).append('#').append(name); if (args.length > 0) { sb.append("("); boolean more = false; for(Type t : args) { if (more) { sb.append(", "); } else { more = true; } sb.append(getJavaType(t.getDescriptor())); } sb.append(')'); } return sb.toString(); } public static String getJavaType(String desc) { int arrIndex = desc.lastIndexOf('[') + 1; desc = arrIndex > 0 ? desc.substring(arrIndex) : desc; if (desc.startsWith("L")) { desc = desc.substring(1, desc.length() - 1).replace('/', '.'); } else { for(Map.Entry<String, String> entry : primitives.entrySet()) { if (entry.getValue().equals(desc)) { desc = entry.getKey(); break; } } } StringBuilder sb = new StringBuilder(desc); for(int i=0;i<arrIndex;i++) { sb.append("[]"); } return sb.toString(); } public static String objectOrArrayType(String type) { StringBuilder buf = new StringBuilder(); int index = 0; while ((index = type.indexOf("[]", index) + 1) > 0) { buf.append('['); } String t = type.substring(0, type.length() - buf.length() * 2); String desc = primitives.get(t); if (desc != null) { buf.append(desc); } else { buf.append('L'); if (t.indexOf('.') < 0) { buf.append(t); } else { buf.append(t.replace('.', '/')); } buf.append(';'); } return buf.toString(); } }