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 java.io.IOException; 034import java.lang.reflect.Method; 035import java.lang.reflect.Modifier; 036import java.util.HashMap; 037import java.util.Map; 038import java.util.Set; 039import java.util.WeakHashMap; 040import java.util.regex.Pattern; 041import org.bridj.BridJ; 042import org.bridj.Platform; 043import org.bridj.Version; 044 045/** 046 * 047 * @author ochafik 048 */ 049public class JNIUtils { 050 051 private static class NativeMethodsCache { 052 053 Map<String, String> signatures = new HashMap<String, String>(); 054 055 public NativeMethodsCache(String internalClassName) throws IOException { 056 for (String[] sig : BytecodeAnalyzer.getNativeMethodSignatures(internalClassName, Platform.getClassLoader())) { 057 signatures.put(sig[1], sig[2]); 058 } 059 } 060 061 public String get(String name) { 062 return signatures.get(name); 063 } 064 065 public Set<String> getNames() { 066 return signatures.keySet(); 067 } 068 } 069 private static Map<String, NativeMethodsCache> nativeMethodsCache = new WeakHashMap<String, NativeMethodsCache>(); 070 071 private static synchronized NativeMethodsCache getNativeMethodsCache(String internalClassName) throws IOException { 072 NativeMethodsCache cache = nativeMethodsCache.get(internalClassName); 073 if (cache == null) { 074 nativeMethodsCache.put(internalClassName, cache = new NativeMethodsCache(internalClassName)); 075 } 076 return cache; 077 } 078 private static final String bridjPackage = 079 BridJ.class.getPackage() == null ? "org.bridj" : BridJ.class.getPackage().getName(); 080 private static final String bridjNormalPackagePrefix = bridjPackage.endsWith(Version.VERSION_SPECIFIC_SUB_PACKAGE) ? bridjPackage.substring(0, bridjPackage.length() - Version.VERSION_SPECIFIC_SUB_PACKAGE.length()) : bridjPackage + "."; 081 private static final String bridjVersionSpecificPackagePrefix = bridjPackage + "."; 082 083 static int findLastNonEscapeUnderscore(String s) { 084 int len = s.length(), i = len; 085 do { 086 i = s.lastIndexOf("_", i - 1); 087 if (i >= 0 && (i == len - 1 || !Character.isDigit(s.charAt(i + 1)))) { 088 return i; 089 } 090 } while (i > 0); 091 return -1; 092 } 093 094 public static String decodeVersionSpecificMethodNameClassAndSignature(String symbolName, Object[] nameAndSigArray) throws NoSuchMethodException, IOException { 095 return decodeMethodNameClassAndSignature(symbolName, nameAndSigArray, bridjNormalPackagePrefix, bridjVersionSpecificPackagePrefix); 096 } 097 098 static String decodeMethodNameClassAndSignature(String symbolName, Object[] nameAndSigArray, String normalClassPrefix, String replacementClassPrefix) throws NoSuchMethodException, IOException { 099 if (symbolName.startsWith("_")) { 100 symbolName = symbolName.substring(1); 101 } 102 if (symbolName.startsWith("Java_")) { 103 symbolName = symbolName.substring("Java_".length()); 104 } 105 106 int i = findLastNonEscapeUnderscore(symbolName); 107 String className = symbolName.substring(0, i).replace('_', '.'); 108 if (normalClassPrefix != null) { 109 if (className.startsWith(normalClassPrefix) && !className.startsWith(replacementClassPrefix)) { 110 className = replacementClassPrefix + className.substring(normalClassPrefix.length()); 111 } 112 } 113 String methodName = symbolName.substring(i + 1).replaceAll("_1", "_"); 114 115 NativeMethodsCache mc = getNativeMethodsCache(className.replace('.', '/')); 116 String sig = mc.get(methodName); 117 if (sig == null) { 118 throw new NoSuchMethodException("Method " + methodName + " not found in class " + className + " : known method names = " + StringUtils.implode(mc.getNames(), ", ")); 119 } 120 121 nameAndSigArray[0] = methodName; 122 nameAndSigArray[1] = sig; 123 124 String internalClassName = className.replace('.', '/'); 125 return internalClassName;//className; 126 127 } 128 129 public static String getNativeName(Class c) { 130 return c.getName().replace('.', '/'); 131 } 132 133 public static String getNativeSignature(Method m) { 134 StringBuffer b = new StringBuffer(); 135 b.append('('); 136 for (Class c : m.getParameterTypes()) { 137 b.append(getNativeSignature(c)); 138 } 139 b.append(')'); 140 b.append(getNativeSignature(m.getReturnType())); 141 return b.toString(); 142 } 143 144 public static String getNativeSignature(Class c) { 145 if (c.isPrimitive()) { 146 if (c == int.class) { 147 return "I"; 148 } 149 if (c == long.class) { 150 return "J"; 151 } 152 if (c == short.class) { 153 return "S"; 154 } 155 if (c == byte.class) { 156 return "B"; 157 } 158 if (c == boolean.class) { 159 return "Z"; 160 } 161 if (c == double.class) { 162 return "D"; 163 } 164 if (c == float.class) { 165 return "F"; 166 } 167 if (c == char.class) { 168 return "C"; 169 } 170 if (c == void.class) { 171 return "V"; 172 } 173 174 throw new RuntimeException("unexpected case"); 175 } 176 if (c.isArray()) { 177 return "[" + getNativeSignature(c.getComponentType()); 178 } 179 return "L" + getNativeName(c) + ";"; 180 } 181}