/* * Copyright (c) 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.org.objectweb.asm.AnnotationVisitor; import com.sun.btrace.org.objectweb.asm.Attribute; import com.sun.btrace.org.objectweb.asm.ClassReader; import com.sun.btrace.org.objectweb.asm.ClassVisitor; import com.sun.btrace.org.objectweb.asm.Opcodes; import com.sun.btrace.org.objectweb.asm.Type; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Collection; import java.util.HashSet; /** * A hacked version of <a href="http://asm.ow2.org/asm50/javadoc/user/org/objectweb/asm/ClassReader.html">ClassReader</a> * allowing fast access to class name, class version, super type, interfaces and annotations. * @author Jaroslav Bachorik */ final class BTraceClassReader extends ClassReader { private static final Method getAttributesMthd; private static final Method readAnnotationValuesMthd; private static final Field itemsFld; static { Method m1 = null, m2 = null; Field f = null; try { m1 = ClassReader.class.getDeclaredMethod("a"); m1.setAccessible(true); m2 = ClassReader.class.getDeclaredMethod( "a", int.class, char[].class, boolean.class, AnnotationVisitor.class ); m2.setAccessible(true); f = ClassReader.class.getDeclaredField("a"); f.setAccessible(true); } catch (Exception e) { e.printStackTrace(); } getAttributesMthd = m1; readAnnotationValuesMthd = m2; itemsFld = f; } /** * Dummy, non-stack-collecting runtime exception. It is used for execution control in ClassReader instances in order * to avoid processing the complete class file when the relevant info is available right at the beginning of * parsing. */ private static final class BailoutException extends RuntimeException { /** * Shared instance to optimize the cost of throwing */ private static final BailoutException INSTANCE = new BailoutException(); private BailoutException() { } @Override public synchronized Throwable fillInStackTrace() { // we don't need the stack here return this; } } private final ClassLoader cl; BTraceClassReader(ClassLoader cl, byte[] bytes) { super(bytes); this.cl = cl; } BTraceClassReader(ClassLoader cl, InputStream in) throws IOException { super(in); this.cl = cl; } public ClassLoader getClassLoader() { return cl; } /** * The associated Java class name ('.' is the package delimiter) * @return */ public String getJavaClassName() { return getClassName().replace('/', '.'); } public String[] readClassSupers() { int u = header; // current offset in the class file char[] c = new char[getMaxStringLength()]; // buffer used to read strings // reads the class declaration int ifcsLen = readUnsignedShort(u + 6); String[] info = new String[ifcsLen + 1]; // supertype name info[0] = readClass(u + 4, c); u += 8; // interfaces, if any for (int i = 0; i < ifcsLen; ++i) { info[1 + i] = readClass(u, c); u += 2; } return info; } public boolean isInterface() { return (getAccess() & Opcodes.ACC_INTERFACE) != 0; } public boolean isBTrace() { return getAnnotationTypes().contains(Constants.BTRACE_DESC); } public Collection<String> getAnnotationTypes() { Collection<String> types = new HashSet<>(); char[] c = new char[getMaxStringLength()]; // buffer used to read strings int anns = getAnnotationsOffset(c); if (anns != -1) { for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { types.add(Type.getType(readUTF8(v, c)).getClassName()); v = skipAnnotationValues(v + 2, c); if (v == -1) break; } } return types; } public int getClassVersion() { try { if (itemsFld != null) { int[] items = (int[])itemsFld.get(this); return readInt(items[1] - 7); } } catch (Exception e) { e.printStackTrace(); } return 51; // default to Java 7 } @Override public void accept(ClassVisitor cv, Attribute[] atrbts, int i) { try { super.accept(cv, atrbts, i); } catch (BailoutException e) { // expected; ignore } } @Override public void accept(ClassVisitor cv, int i) { try { super.accept(cv, i); } catch (BailoutException e) { // expected; ignore } } public static void bailout() { throw BailoutException.INSTANCE; } private int getAttributes() { try { if (getAttributesMthd != null) { return (int)getAttributesMthd.invoke(this); } } catch (Exception e) { e.printStackTrace(); } return -1; } private int getAnnotationsOffset(char[] buf) { int u = getAttributes(); if (u == -1) { return -1; } for (int i = readUnsignedShort(u); i > 0; --i) { String attrName = readUTF8(u + 2, buf); if ("RuntimeVisibleAnnotations".equals(attrName)) { return u + 8; } u += 6 + readInt(u + 4); } return -1; } private int skipAnnotationValues(int off, char[] buf) { try { if (readAnnotationValuesMthd != null) { return (int) readAnnotationValuesMthd.invoke(this, off, buf, true, null); } } catch (Exception e) { e.printStackTrace(); } return -1; } }