001/* 002 * BridJ - Dynamic and blazing-fast native interop for Java. 003 * http://bridj.googlecode.com/ 004 * 005 * Copyright (c) 2010-2013, Olivier Chafik (http://ochafik.com/) 006 * All rights reserved. 007 * 008 * Redistribution and use in source and binary forms, with or without 009 * modification, are permitted provided that the following conditions are met: 010 * 011 * * Redistributions of source code must retain the above copyright 012 * notice, this list of conditions and the following disclaimer. 013 * * Redistributions in binary form must reproduce the above copyright 014 * notice, this list of conditions and the following disclaimer in the 015 * documentation and/or other materials provided with the distribution. 016 * * Neither the name of Olivier Chafik nor the 017 * names of its contributors may be used to endorse or promote products 018 * derived from this software without specific prior written permission. 019 * 020 * THIS SOFTWARE IS PROVIDED BY OLIVIER CHAFIK AND CONTRIBUTORS ``AS IS'' AND ANY 021 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 022 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 023 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY 024 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 025 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 026 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 027 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 028 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 029 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 030 */ 031package org.bridj.util; 032 033import org.bridj.Platform; 034import java.io.ByteArrayOutputStream; 035import java.io.FileNotFoundException; 036import java.io.IOException; 037import java.io.InputStream; 038import java.lang.reflect.Modifier; 039import java.net.URL; 040import java.util.ArrayList; 041import java.util.Arrays; 042import java.util.EnumSet; 043import java.util.List; 044 045import org.objectweb.asm.*; 046import static org.objectweb.asm.ClassReader.*; 047 048/** 049 * Util class that scavenges through a class' bytecode to retrieve the original 050 * order of fields and methods, as defined in the sources (unlike what the 051 * reflection APIs return : they don't guarantee the order). 052 * 053 * @author ochafik 054 */ 055public final class BytecodeAnalyzer { 056 057 private BytecodeAnalyzer() { 058 } 059 060 /** 061 * Returns array of String[] { declaringClassInternalName, methodName, 062 * signature } 063 */ 064 public static List<String[]> getNativeMethodSignatures(Class c) throws IOException { 065 return getNativeMethodSignatures(getInternalName(c), Platform.getClassLoader(c)); 066 } 067 068 /** 069 * Returns array of String[] { declaringClassInternalName, methodName, 070 * signature } 071 */ 072 public static List<String[]> getNativeMethodSignatures(String internalName, ClassLoader classLoader) throws IOException { 073 return getNativeMethodSignatures(internalName, classLoader, new ArrayList<String[]>()); 074 } 075 076 private static List<String[]> getNativeMethodSignatures(final String internalName, ClassLoader classLoader, final List<String[]> ret) throws IOException { 077 ClassReader r = new ClassReader(readByteCode(internalName, classLoader)); 078 String p = r.getSuperName(); 079 if (p != null && !p.equals("java/lang/Object")) { 080 getNativeMethodSignatures(p, classLoader, ret); 081 } 082 083 r.accept(new EmptyVisitor() { 084 @Override 085 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 086 if (Modifier.isNative(access)) { 087 ret.add(new String[]{internalName, name, desc}); 088 } 089 return null; 090 } 091 }, SKIP_DEBUG | SKIP_FRAMES | SKIP_CODE); 092 093 return ret; 094 } 095 096 private static List<String> getFieldNames(final String internalName, String recurseToInternalName, ClassLoader classLoader, final List<String> ret) throws IOException { 097 ClassReader r = new ClassReader(readByteCode(internalName, classLoader)); 098 String p = r.getSuperName(); 099 if (p != null && !p.equals("java/lang/Object") && !recurseToInternalName.equals(internalName)) { 100 getFieldNames(p, recurseToInternalName, classLoader, ret); 101 } 102 103 r.accept(new EmptyVisitor() { 104 @Override 105 public FieldVisitor visitField(int i, String name, String string1, String string2, Object o) { 106 ret.add(name); 107 return null; 108 } 109 }, SKIP_DEBUG | SKIP_FRAMES | SKIP_CODE); 110 111 return ret; 112 } 113 114 private static List<String> getMethodNames(final String internalName, String recurseToInternalName, ClassLoader classLoader, final List<String> ret) throws IOException { 115 ClassReader r = new ClassReader(readByteCode(internalName, classLoader)); 116 String p = r.getSuperName(); 117 if (p != null && !p.equals("java/lang/Object") && !recurseToInternalName.equals(internalName)) { 118 getMethodNames(p, recurseToInternalName, classLoader, ret); 119 } 120 121 r.accept(new EmptyVisitor() { 122 @Override 123 public MethodVisitor visitMethod(int i, String name, String string1, String string2, String[] strings) { 124 ret.add(name); 125 return null; 126 } 127 }, SKIP_DEBUG | SKIP_FRAMES | SKIP_CODE); 128 129 return ret; 130 } 131 132 static String getInternalName(Class c) { 133 return c.getName().replace('.', '/'); 134 } 135 136 static URL getClassResource(Class c) throws FileNotFoundException { 137 return getClassResource(getInternalName(c), Platform.getClassLoader(c)); 138 } 139 140 static URL getClassResource(String internalClassName, ClassLoader classLoader) throws FileNotFoundException { 141 String p = internalClassName + ".class"; 142 URL u = classLoader.getResource(p); 143 if (u == null) { 144 throw new FileNotFoundException("Resource '" + p + "'"); 145 } 146 return u; 147 } 148 149 static byte[] readByteCode(String classInternalName, ClassLoader classLoader) throws FileNotFoundException, IOException { 150 return readBytes(getClassResource(classInternalName, classLoader).openStream(), true); 151 } 152 153 static byte[] readBytes(InputStream in, boolean close) throws IOException { 154 ByteArrayOutputStream out = new ByteArrayOutputStream(); 155 byte[] b = new byte[1024]; 156 int len; 157 while ((len = in.read(b)) > 0) { 158 out.write(b, 0, len); 159 } 160 if (close) { 161 in.close(); 162 } 163 return out.toByteArray(); 164 } 165 166 public static List<String> getFieldNames(Class c, Class recurseTo) throws IOException { 167 return getFieldNames(getInternalName(c), getInternalName(recurseTo), Platform.getClassLoader(c), new ArrayList<String>()); 168 } 169 170 public static List<String> getMethodNames(Class c, Class recurseTo) throws IOException { 171 return getMethodNames(getInternalName(c), getInternalName(recurseTo), Platform.getClassLoader(c), new ArrayList<String>()); 172 } 173 174 static class EmptyVisitor extends ClassVisitor { 175 176 public EmptyVisitor() { 177 super(Opcodes.ASM4); 178 } 179 180 public void visit(int i, int i1, String string, String string1, String string2, String[] strings) { 181 } 182 183 public void visitSource(String string, String string1) { 184 } 185 186 public void visitOuterClass(String string, String string1, String string2) { 187 } 188 189 public AnnotationVisitor visitAnnotation(String string, boolean bln) { 190 return null; 191 } 192 193 public void visitAttribute(Attribute atrbt) { 194 } 195 196 public void visitInnerClass(String string, String string1, String string2, int i) { 197 } 198 199 public FieldVisitor visitField(int i, String string, String string1, String string2, Object o) { 200 return null; 201 } 202 203 public MethodVisitor visitMethod(int i, String string, String string1, String string2, String[] strings) { 204 return null; 205 } 206 207 public void visitEnd() { 208 } 209 } 210}