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; 032 033import org.bridj.ann.Forwardable; 034 035import java.util.Set; 036import java.util.HashSet; 037 038import org.bridj.util.Utils; 039 040import static org.bridj.util.AnnotationUtils.*; 041import static org.bridj.util.Utils.*; 042 043import java.io.File; 044import java.io.FileNotFoundException; 045import java.io.IOException; 046import java.lang.annotation.Annotation; 047import java.lang.reflect.AnnotatedElement; 048import java.lang.reflect.Member; 049import java.lang.reflect.Method; 050import java.util.ArrayList; 051import java.util.Arrays; 052import java.util.Enumeration; 053import java.util.HashMap; 054import java.util.Iterator; 055import java.util.List; 056import java.util.Map; 057import java.util.concurrent.ConcurrentHashMap; 058import java.util.logging.Level; 059import java.util.logging.Logger; 060import java.util.regex.*; 061 062import org.bridj.BridJRuntime.TypeInfo; 063import org.bridj.demangling.Demangler.Symbol; 064import org.bridj.demangling.Demangler.MemberRef; 065import org.bridj.ann.Library; 066 067import java.util.Stack; 068import java.io.PrintWriter; 069import java.lang.reflect.Type; 070import java.net.URL; 071 072import org.bridj.util.StringUtils; 073 074import static org.bridj.Platform.*; 075import static java.lang.System.*; 076 077import org.bridj.util.ClassDefiner; 078import org.bridj.util.ASMUtils; 079 080/// http://www.codesourcery.com/public/cxx-abi/cxx-vtable-ex.html 081/** 082 * BridJ's central class.<br> 083 * <ul> 084 * <li>To register a class with native methods (which can be in inner classes), 085 * just add the following static block to your class : 086 * <pre>{@code 087 * static { 088 * BridJ.register(); 089 * } 090 * }</pre> 091 * </li><li>You can also register a class explicitely with 092 * {@link BridJ#register(java.lang.Class)} 093 * </li><li>To alter the name of a library, use 094 * {@link BridJ#setNativeLibraryActualName(String, String)} and 095 * {@link BridJ#addNativeLibraryAlias(String, String)} 096 * </li> 097 * </ul> 098 * 099 * @author ochafik 100 */ 101public class BridJ { 102 103 static final Map<AnnotatedElement, NativeLibrary> librariesByClass = new HashMap<AnnotatedElement, NativeLibrary>(); 104 static final Map<String, File> librariesFilesByName = new HashMap<String, File>(); 105 static final Map<File, NativeLibrary> librariesByFile = new HashMap<File, NativeLibrary>(); 106 private static NativeEntities orphanEntities = new NativeEntities(); 107 static final Map<Class<?>, BridJRuntime> classRuntimes = new HashMap<Class<?>, BridJRuntime>(); 108 static final Map<Long, NativeObject> strongNativeObjects = new HashMap<Long, NativeObject>(); 109 110 public static long sizeOf(Type type) { 111 Class c = Utils.getClass(type); 112 if (c.isPrimitive()) { 113 return StructUtils.primTypeLength(c); 114 } else if (Pointer.class.isAssignableFrom(c)) { 115 return Pointer.SIZE; 116 } else if (c == CLong.class) { 117 return CLong.SIZE; 118 } else if (c == TimeT.class) { 119 return TimeT.SIZE; 120 } else if (c == SizeT.class) { 121 return SizeT.SIZE; 122 } else if (c == Integer.class || c == Float.class) { 123 return 4; 124 } else if (c == Character.class || c == Short.class) { 125 return 2; 126 } else if (c == Long.class || c == Double.class) { 127 return 8; 128 } else if (c == Boolean.class || c == Byte.class) { 129 return 1; 130 } else if (NativeObject.class.isAssignableFrom(c)) { 131 return getRuntime(c).getTypeInfo(type).sizeOf(); 132 } else if (IntValuedEnum.class.isAssignableFrom(c)) { 133 return 4; 134 } 135 /*if (o instanceof NativeObject) { 136 NativeObject no = (NativeObject)o; 137 return no.typeInfo.sizeOf(no); 138 }*/ 139 throw new RuntimeException("Unable to compute size of type " + Utils.toString(type)); 140 } 141 142 /** 143 * Keep a hard reference to a native object to avoid its garbage 144 * collection.<br> 145 * See {@link BridJ#unprotectFromGC(NativeObject)} to remove the GC 146 * protection. 147 */ 148 public static synchronized <T extends NativeObject> T protectFromGC(T ob) { 149 long peer = Pointer.getAddress(ob, null); 150 strongNativeObjects.put(peer, ob); 151 return ob; 152 } 153 154 /** 155 * Drop the hard reference created with 156 * {@link BridJ#protectFromGC(NativeObject)}. 157 */ 158 public static synchronized <T extends NativeObject> T unprotectFromGC(T ob) { 159 long peer = Pointer.getAddress(ob, null); 160 NativeObject removed = strongNativeObjects.remove(peer); 161 if (removed != ob) { 162 throw new IllegalStateException("Unprotected object " + removed + " instead of " + ob + " for address " + peer); 163 } 164 return ob; 165 } 166 167 public static void delete(NativeObject nativeObject) { 168 BridJ.setJavaObjectFromNativePeer(Pointer.getAddress(nativeObject, null), null); 169 Pointer.getPointer(nativeObject, null).release(); 170 } 171 172 /** 173 * Registers the native methods of the caller class and all its inner types. 174 * <pre>{@code 175 * \@Library("mylib") 176 * public class MyLib { 177 * static { 178 * BridJ.register(); 179 * } 180 * public static native void someFunc(); 181 * } 182 * }</pre> 183 */ 184 public static synchronized void register() { 185 StackTraceElement[] stackTrace = new Exception().getStackTrace(); 186 if (stackTrace.length < 2) { 187 throw new RuntimeException("No useful stack trace : cannot register with register(), please use register(Class) instead."); 188 } 189 String name = stackTrace[1].getClassName(); 190 try { 191 Class<?> type = Class.forName(name, false, Platform.getClassLoader()); 192 register(type); 193 } catch (Exception ex) { 194 throw new RuntimeException("Failed to register class " + name, ex); 195 } 196 } 197 198 /** 199 * Create a subclass of the provided original class with synchronized 200 * overrides for all native methods. Non-default constructors are not 201 * currently handled. 202 * 203 * @param <T> original class type 204 * @param original class 205 * @throws IOException 206 */ 207 public static <T> Class<? extends T> subclassWithSynchronizedNativeMethods(Class<T> original) throws IOException { 208 ClassDefiner classDefiner = getRuntimeByRuntimeClass(CRuntime.class).getCallbackNativeImplementer(); 209 return ASMUtils.createSubclassWithSynchronizedNativeMethodsAndNoStaticFields(original, classDefiner); 210 } 211 212 enum CastingType { 213 214 None, CastingNativeObject, CastingNativeObjectReturnType 215 } 216 static ThreadLocal<Stack<CastingType>> currentlyCastingNativeObject = new ThreadLocal<Stack<CastingType>>() { 217 @Override 218 protected java.util.Stack<CastingType> initialValue() { 219 Stack<CastingType> s = new Stack<CastingType>(); 220 s.push(CastingType.None); 221 return s; 222 } 223 ; 224 225 }; 226 227 @Deprecated 228 public static boolean isCastingNativeObjectInCurrentThread() { 229 return currentlyCastingNativeObject.get().peek() != CastingType.None; 230 } 231 232 @Deprecated 233 public static boolean isCastingNativeObjectReturnTypeInCurrentThread() { 234 return currentlyCastingNativeObject.get().peek() == CastingType.CastingNativeObjectReturnType; 235 } 236 237 238 private static final ConcurrentHashMap<Long, NativeObject> registeredObjects = 239 new ConcurrentHashMap<Long, NativeObject>(); 240 241 public static synchronized <O extends NativeObject> void setJavaObjectFromNativePeer(long peer, O object) { 242 if (object == null) { 243 registeredObjects.remove(peer); 244 } else { 245 registeredObjects.put(peer, object); 246 } 247 } 248 249 public static synchronized Object getJavaObjectFromNativePeer(long peer) { 250 return registeredObjects.get(peer); 251 } 252 253 private static <O extends NativeObject> O createNativeObjectFromPointer(Pointer<? super O> pointer, Type type, CastingType castingType) { 254 Stack<CastingType> s = currentlyCastingNativeObject.get(); 255 s.push(castingType); 256 try { 257 BridJRuntime runtime = getRuntime(Utils.getClass(type)); 258 TypeInfo<O> typeInfo = getTypeInfo(runtime, type); 259 O instance = typeInfo.cast(pointer); 260 if (BridJ.debug) { 261 BridJ.info("Created native object from pointer " + pointer); 262 } 263 return instance; 264 } catch (Exception ex) { 265 throw new RuntimeException("Failed to cast pointer to native object of type " + Utils.getClass(type).getName(), ex); 266 } finally { 267 s.pop(); 268 } 269 } 270 271 public static <O extends NativeObject> void copyNativeObjectToAddress(O value, Type type, Pointer<O> ptr) { 272 BridJRuntime runtime = getRuntime(Utils.getClass(type)); 273 getTypeInfo(runtime, type).copyNativeObjectToAddress(value, (Pointer) ptr); 274 } 275 276 public static <O extends NativeObject> O createNativeObjectFromPointer(Pointer<? super O> pointer, Type type) { 277 return (O) createNativeObjectFromPointer(pointer, type, CastingType.CastingNativeObject); 278 } 279 280 public static <O extends NativeObject> O createNativeObjectFromReturnValuePointer(Pointer<? super O> pointer, Type type) { 281 return (O) createNativeObjectFromPointer(pointer, type, CastingType.CastingNativeObjectReturnType); 282 } 283 private static Map<Class<? extends BridJRuntime>, BridJRuntime> runtimes = new HashMap<Class<? extends BridJRuntime>, BridJRuntime>(); 284 285 public static synchronized <R extends BridJRuntime> R getRuntimeByRuntimeClass(Class<R> runtimeClass) { 286 R r = (R) runtimes.get(runtimeClass); 287 if (r == null) { 288 try { 289 runtimes.put(runtimeClass, r = runtimeClass.newInstance()); 290 } catch (Exception e) { 291 throw new RuntimeException("Failed to instantiate runtime " + runtimeClass.getName(), e); 292 } 293 } 294 295 return r; 296 } 297 298 /** 299 * Get the runtime class associated with a class (using the 300 * {@link org.bridj.ann.Runtime} annotation, if any, looking up parents and 301 * defaulting to {@link org.bridj.CRuntime}). 302 */ 303 public static Class<? extends BridJRuntime> getRuntimeClass(Class<?> type) { 304 org.bridj.ann.Runtime runtimeAnn = getInheritableAnnotation(org.bridj.ann.Runtime.class, type); 305 Class<? extends BridJRuntime> runtimeClass = null; 306 if (runtimeAnn != null) { 307 runtimeClass = runtimeAnn.value(); 308 } else { 309 runtimeClass = CRuntime.class; 310 } 311 312 return runtimeClass; 313 } 314 315 /** 316 * Get the runtime associated with a class (using the 317 * {@link org.bridj.ann.Runtime} annotation, if any, looking up parents and 318 * defaulting to {@link org.bridj.CRuntime}). 319 */ 320 public static BridJRuntime getRuntime(Class<?> type) { 321 synchronized (classRuntimes) { 322 BridJRuntime runtime = classRuntimes.get(type); 323 if (runtime == null) { 324 Class<? extends BridJRuntime> runtimeClass = getRuntimeClass(type); 325 runtime = getRuntimeByRuntimeClass(runtimeClass); 326 classRuntimes.put(type, runtime); 327 328 if (veryVerbose) { 329 info("Runtime for " + type.getName() + " : " + runtimeClass.getName()); 330 } 331 } 332 return runtime; 333 } 334 } 335 336 /** 337 * Registers the native method of a type (and all its inner types). 338 * <pre>{@code 339 * \@Library("mylib") 340 * public class MyLib { 341 * static { 342 * BridJ.register(MyLib.class); 343 * } 344 * public static native void someFunc(); 345 * } 346 * }</pre> 347 */ 348 public static BridJRuntime register(Class<?> type) { 349 BridJRuntime runtime = getRuntime(type); 350 if (runtime == null) { 351 for (Class<?> child : type.getClasses()) { 352 register(child); 353 } 354 } else { 355 runtime.register(type); 356 } 357 return runtime; 358 } 359 360 public static void unregister(Class<?> type) { 361 BridJRuntime runtime = getRuntime(type); 362 if (runtime == null) { 363 for (Class<?> child : type.getClasses()) { 364 register(child); 365 } 366 } else { 367 runtime.unregister(type); 368 } 369 } 370 static Map<Type, TypeInfo<?>> typeInfos = new HashMap<Type, TypeInfo<?>>(); 371 372 static <T extends NativeObject> TypeInfo<T> getTypeInfo(BridJRuntime runtime, Type t) { 373 synchronized (typeInfos) { 374 TypeInfo info = typeInfos.get(t); 375 if (info == null) { 376 // getRuntime(Utils.getClass(t)) 377 info = runtime.getTypeInfo(t); 378 typeInfos.put(t, info); 379 } 380 return info; 381 } 382 } 383 384 enum Switch { 385 386 Debug("bridj.debug", "BRIDJ_DEBUG", false, 387 "Debug mode (implies high verbosity)"), 388 DebugNeverFree("bridj.debug.neverFree", "BRIDJ_DEBUG_NEVER_FREE", false, 389 "Never free allocated pointers (deprecated)"), 390 DebugPointers("bridj.debug.pointers", "BRIDJ_DEBUG_POINTERS", false, 391 "Trace pointer allocations & deallocations (to debug memory issues)"), 392 DebugPointerReleases("bridj.debug.pointer.releases", "BRIDJ_DEBUG_POINTER_RELEASES", false, 393 "Prevent double releases of pointers and keep the trace of their first release (to debug memory issues)"), 394 VeryVerbose("bridj.veryVerbose", "BRIDJ_VERY_VERBOSE", false, 395 "Highly verbose mode"), 396 Verbose("bridj.verbose", "BRIDJ_VERBOSE", false, 397 "Verbose mode"), 398 Quiet("bridj.quiet", "BRIDJ_QUIET", false, 399 "Quiet mode"), 400 CachePointers("bridj.cache.pointers", "BRIDJ_CACHE_POINTERS", true, 401 "Cache last recently used pointers in each thread"), 402 AlignDouble("bridj.alignDouble", "BRIDJ_ALIGN_DOUBLE", false, 403 "Align doubles on 8 bytes boundaries even on Linux 32 bits (see -malign-double GCC option)."), 404 LogCalls("bridj.logCalls", "BRIDJ_LOG_CALLS", false, 405 "Log each native call performed (or call from native to Java callback)"), 406 WarnStructFields("bridj.warnStructFields", "BRIDJ_WARN_STRUCT_FIELDS", true, 407 "Warn when struct fields are implemented with Java fields instead of methods"), 408 Protected("bridj.protected", "BRIDJ_PROTECTED", false, 409 "Protect all native calls (including memory accesses) against native crashes (disables assembly optimizations and adds quite some overhead)."), 410 Destructors("bridj.destructors", "BRIDJ_DESTRUCTORS", true, 411 "Enable destructors (in languages that support them, such as C++)"), 412 Direct("bridj.direct", "BRIDJ_DIRECT", true, 413 "Direct mode (uses optimized assembler glue when possible to speed up calls)"), 414 StructsByValue("bridj.structsByValue", "BRIDJ_STRUCT_BY_VALUE", false, 415 "Enable experimental support for structs-by-value arguments and return values for C/C++ functions and methods."); 416 public final boolean enabled, enabledByDefault; 417 public final String propertyName, envName, description; 418 419 /** 420 * Important : keep full property name and environment variable name to 421 * enable full-text search of options !!! 422 */ 423 Switch(String propertyName, String envName, boolean enabledByDefault, String description) { 424 if (enabledByDefault) { 425 enabled = !("false".equals(getProperty(propertyName)) || "0".equals(getenv(envName))); 426 } else { 427 enabled = "true".equals(getProperty(propertyName)) || "1".equals(getenv(envName)); 428 } 429 430 this.enabledByDefault = enabledByDefault; 431 this.propertyName = propertyName; 432 this.envName = envName; 433 this.description = description; 434 } 435 436 public String getFullDescription() { 437 return envName + " / " + propertyName + " (" + (enabledByDefault ? "enabled" : "disabled") + " by default) :\n\t" + description.replaceAll("\n", "\n\t"); 438 } 439 } 440 441 static { 442 checkOptions(); 443 } 444 445 static void checkOptions() { 446 Set<String> props = new HashSet<String>(), envs = new HashSet<String>(); 447 for (Switch s : Switch.values()) { 448 props.add(s.propertyName); 449 envs.add(s.envName); 450 } 451 boolean hasUnknown = false; 452 for (String n : System.getenv().keySet()) { 453 if (!n.startsWith("BRIDJ_") || envs.contains(n)) { 454 continue; 455 } 456 457 if (n.endsWith("_LIBRARY")) { 458 continue; 459 } 460 461 if (n.endsWith("_DEPENDENCIES")) { 462 continue; 463 } 464 465 error("Unknown environment variable : " + n + "=\"" + System.getenv(n) + "\""); 466 hasUnknown = true; 467 } 468 469 for (Enumeration<String> e = (Enumeration) System.getProperties().propertyNames(); e.hasMoreElements();) { 470 String n = e.nextElement(); 471 if (!n.startsWith("bridj.") || props.contains(n)) { 472 continue; 473 } 474 475 if (n.endsWith(".library")) { 476 continue; 477 } 478 479 if (n.endsWith(".dependencies")) { 480 continue; 481 } 482 483 error("Unknown property : " + n + "=\"" + System.getProperty(n) + "\""); 484 hasUnknown = true; 485 } 486 if (hasUnknown) { 487 StringBuilder b = new StringBuilder(); 488 b.append("Available options (ENVIRONMENT_VAR_NAME / javaPropertyName) :\n"); 489 for (Switch s : Switch.values()) { 490 b.append(s.getFullDescription() + "\n"); 491 } 492 error(b.toString()); 493 } 494 } 495 public static final boolean debug = Switch.Debug.enabled; 496 public static final boolean debugNeverFree = Switch.DebugNeverFree.enabled; 497 public static final boolean debugPointers = Switch.DebugPointers.enabled; 498 public static final boolean debugPointerReleases = Switch.DebugPointerReleases.enabled || debugPointers; 499 public static final boolean veryVerbose = Switch.VeryVerbose.enabled; 500 public static final boolean verbose = debug || veryVerbose || Switch.Verbose.enabled; 501 public static final boolean quiet = Switch.Quiet.enabled; 502 public static final boolean logCalls = Switch.LogCalls.enabled; 503 public static final boolean warnStructFields = Switch.LogCalls.enabled; 504 public static final boolean protectedMode = Switch.Protected.enabled; 505 public static final boolean enableDestructors = Switch.Destructors.enabled; 506 public static final boolean alignDoubles = Switch.AlignDouble.enabled; 507 public static final boolean cachePointers = Switch.CachePointers.enabled; 508 static volatile int minLogLevelValue = (verbose ? Level.WARNING : Level.INFO).intValue(); 509 510 public static void setMinLogLevel(Level level) { 511 minLogLevelValue = level.intValue(); 512 } 513 514 static boolean shouldLog(Level level) { 515 return !quiet && (verbose || level.intValue() >= minLogLevelValue); 516 } 517 static Logger logger; 518 519 static synchronized Logger getLogger() { 520 if (logger == null) { 521 logger = Logger.getLogger(BridJ.class.getName()); 522 } 523 return logger; 524 } 525 526 public static boolean info(String message) { 527 return info(message, null); 528 } 529 530 public static boolean info(String message, Throwable ex) { 531 return log(Level.INFO, message, ex); 532 } 533 534 public static boolean debug(String message) { 535 if (!debug) { 536 return true; 537 } 538 return info(message, null); 539 } 540 541 public static boolean error(String message) { 542 return error(message, null); 543 } 544 545 public static boolean error(String message, Throwable ex) { 546 return log(Level.INFO, message, ex); 547 } 548 549 public static boolean warning(String message) { 550 return warning(message, null); 551 } 552 553 public static boolean warning(String message, Throwable ex) { 554 return log(Level.INFO, message, ex); 555 } 556 557 private static boolean log(Level level, String message, Throwable ex) { 558 if (!shouldLog(level)) { 559 return true; 560 } 561 getLogger().log(level, message, ex); 562 return true; 563 } 564 565 static void logCall(Method m) { 566 info("Calling method " + m); 567 } 568 569 public static synchronized NativeEntities getNativeEntities(AnnotatedElement type) throws IOException { 570 NativeLibrary lib = getNativeLibrary(type); 571 if (lib != null) { 572 return lib.getNativeEntities(); 573 } 574 return getOrphanEntities(); 575 } 576 577 public static synchronized NativeLibrary getNativeLibrary(AnnotatedElement type) throws IOException { 578 NativeLibrary lib = librariesByClass.get(type); 579 if (lib == null) { 580 Library libraryAnnotation = getLibrary(type); 581 if (libraryAnnotation != null) { 582 String libraryName = libraryAnnotation.value(); 583 String dependenciesEnv = getDependenciesEnv(libraryName); 584 List<String> dependencies = libraryDependencies.get(libraryName); 585 List<String> staticDependencies = Arrays.asList( 586 dependenciesEnv == null ? libraryAnnotation.dependencies() : dependenciesEnv.split(",")); 587 if (dependencies == null) { 588 dependencies = staticDependencies; 589 } else { 590 dependencies.addAll(staticDependencies); 591 } 592 593 for (String dependency : dependencies) { 594 if (verbose) { 595 info("Trying to load dependency '" + dependency + "' of '" + libraryName + "'"); 596 } 597 NativeLibrary depLib = getNativeLibrary(dependency); 598 if (depLib == null) { 599 throw new RuntimeException("Failed to load dependency '" + dependency + "' of library '" + libraryName + "'"); 600 } 601 } 602 lib = getNativeLibrary(libraryName); 603 if (lib != null) { 604 librariesByClass.put(type, lib); 605 } 606 } 607 } 608 return lib; 609 } 610 611 /** 612 * Reclaims all the memory allocated by BridJ in the JVM and on the native 613 * side. 614 */ 615 public synchronized static void releaseAll() { 616 strongNativeObjects.clear(); 617 gc(); 618 619 for (NativeLibrary lib : librariesByFile.values()) { 620 lib.release(); 621 } 622 librariesByFile.clear(); 623 librariesByClass.clear(); 624 getOrphanEntities().release(); 625 gc(); 626 } 627 //public synchronized static void release(Class<?>); 628 629 public synchronized static void releaseLibrary(String name) { 630 File file = librariesFilesByName.remove(name); 631 if (file != null) { 632 releaseLibrary(file); 633 } 634 } 635 636 public synchronized static void releaseLibrary(File library) { 637 NativeLibrary lib = librariesByFile.remove(library); 638 if (lib != null) { 639 lib.release(); 640 } 641 } 642 static Map<String, NativeLibrary> libHandles = new HashMap<String, NativeLibrary>(); 643 static volatile List<String> nativeLibraryPaths; 644 static List<String> additionalPaths = new ArrayList<String>(); 645 646 public static synchronized void addLibraryPath(String path) { 647 additionalPaths.add(path); 648 nativeLibraryPaths = null; // invalidate cached paths 649 } 650 651 private static void addPathsFromEnv(List<String> out, String name) { 652 String env = getenv(name); 653 if (BridJ.verbose) { 654 BridJ.info("Environment var " + name + " = " + env); 655 } 656 addPaths(out, env); 657 } 658 659 private static void addPathsFromProperty(List<String> out, String name) { 660 String env = getProperty(name); 661 if (BridJ.verbose) { 662 BridJ.info("Property " + name + " = " + env); 663 } 664 addPaths(out, env); 665 } 666 667 private static void addPaths(List<String> out, String env) { 668 if (env == null) { 669 return; 670 } 671 672 String[] paths = env.split(File.pathSeparator); 673 if (paths.length == 0) { 674 return; 675 } 676 if (paths.length == 1) { 677 out.add(paths[0]); 678 return; 679 } 680 out.addAll(Arrays.asList(paths)); 681 } 682 683 static synchronized List<String> getNativeLibraryPaths() { 684 if (nativeLibraryPaths == null) { 685 nativeLibraryPaths = new ArrayList<String>(); 686 nativeLibraryPaths.addAll(additionalPaths); 687 nativeLibraryPaths.add(null); 688 nativeLibraryPaths.add("."); 689 690 addPathsFromEnv(nativeLibraryPaths, "LD_LIBRARY_PATH"); 691 addPathsFromEnv(nativeLibraryPaths, "DYLD_LIBRARY_PATH"); 692 addPathsFromEnv(nativeLibraryPaths, "PATH"); 693 addPathsFromProperty(nativeLibraryPaths, "java.library.path"); 694 addPathsFromProperty(nativeLibraryPaths, "sun.boot.library.path"); 695 addPathsFromProperty(nativeLibraryPaths, "gnu.classpath.boot.library.path"); 696 697 File javaHome = new File(getProperty("java.home")); 698 nativeLibraryPaths.add(new File(javaHome, "bin").toString()); 699 if (isMacOSX()) { 700 nativeLibraryPaths.add(new File(javaHome, "../Libraries").toString()); 701 } 702 703 704 if (isUnix()) { 705 String bits = is64Bits() ? "64" : "32"; 706 if (isLinux()) { 707 // First try Ubuntu's multi-arch paths (cf. https://wiki.ubuntu.com/MultiarchSpec) 708 String[] abis = isArm() ? 709 new String[] {"gnueabi", "gnueabihf"} : 710 new String[] {"gnu"}; 711 for (String abi : abis) { 712 String multiArch = getMachine() + "-linux-" + abi; 713 nativeLibraryPaths.add("/lib/" + multiArch); 714 nativeLibraryPaths.add("/usr/lib/" + multiArch); 715 } 716 // Add /usr/lib32 and /lib32 717 nativeLibraryPaths.add("/usr/lib" + bits); 718 nativeLibraryPaths.add("/lib" + bits); 719 } else if (isSolaris()) { 720 // Add /usr/lib/32 and /lib/32 721 nativeLibraryPaths.add("/usr/lib/" + bits); 722 nativeLibraryPaths.add("/lib/" + bits); 723 } 724 725 nativeLibraryPaths.add("/usr/lib"); 726 nativeLibraryPaths.add("/lib"); 727 nativeLibraryPaths.add("/usr/local/lib"); 728 } 729 for (Iterator<String> it = nativeLibraryPaths.iterator(); it.hasNext();) { 730 final String next = it.next(); 731 if (null != next && new File(next).isDirectory()) { 732 continue; 733 } 734 it.remove(); 735 } 736 } 737 return nativeLibraryPaths; 738 } 739 static Map<String, String> libraryActualNames = new HashMap<String, String>(); 740 741 /** 742 * Define the actual name of a library.<br> 743 * Works only before the library is loaded.<br> 744 * For instance, library "OpenGL" is actually named "OpenGL32" on Windows : 745 * BridJ.setNativeLibraryActualName("OpenGL", "OpenGL32"); 746 * 747 * @param name 748 * @param actualName 749 */ 750 public static synchronized void setNativeLibraryActualName(String name, String actualName) { 751 libraryActualNames.put(name, actualName); 752 } 753 static Map<String, List<String>> libraryAliases = new HashMap<String, List<String>>(); 754 755 /** 756 * Add a possible alias for a library.<br> 757 * Aliases are prioritary over the library (or its actual name, see 758 * {@link BridJ#setNativeLibraryActualName(String, String)}), in the order 759 * they are defined.<br> 760 * Works only before the library is loaded.<br> 761 * 762 * @param name 763 * @param alias 764 */ 765 public static synchronized void addNativeLibraryAlias(String name, String alias) { 766 List<String> list = libraryAliases.get(name); 767 if (list == null) { 768 libraryAliases.put(name, list = new ArrayList<String>()); 769 } 770 if (!list.contains(alias)) { 771 list.add(alias); 772 } 773 } 774 static Map<String, List<String>> libraryDependencies = new HashMap<String, List<String>>(); 775 776 /** 777 * Add names of library dependencies for a library.<br> 778 * Works only before the library is loaded.<br> 779 * 780 * @param name 781 * @param dependencyNames 782 */ 783 public static synchronized void addNativeLibraryDependencies(String name, String... dependencyNames) { 784 List<String> list = libraryDependencies.get(name); 785 if (list == null) { 786 libraryDependencies.put(name, list = new ArrayList<String>()); 787 } 788 for (String dependencyName : dependencyNames) { 789 if (!list.contains(dependencyName)) { 790 list.add(dependencyName); 791 } 792 } 793 } 794 private static final Pattern numPat = Pattern.compile("\\b(\\d+)\\b"); 795 796 /** 797 * Given "1.2.3", will yield (1 + 2 / 1000 + 3 / 1000000) 798 */ 799 static double parseVersion(String s) { 800 Matcher m = numPat.matcher(s); 801 double res = 0.0, f = 1; 802 while (m.find()) { 803 res += Integer.parseInt(m.group(1)) * f; 804 f /= 1000; 805 } 806 return res; 807 } 808 809 static File findFileWithGreaterVersion(File dir, String[] files, String baseFileName) { 810 Pattern versionPattern = Pattern.compile(Pattern.quote(baseFileName) + "((:?\\.\\d+)+)"); 811 double maxVersion = 0; 812 String maxVersionFile = null; 813 for (String fileName : files) { 814 Matcher m = versionPattern.matcher(fileName); 815 if (m.matches()) { 816 double version = parseVersion(m.group(1)); 817 if (maxVersionFile == null || version > maxVersion) { 818 maxVersionFile = fileName; 819 maxVersion = version; 820 } 821 } 822 } 823 if (maxVersionFile == null) { 824 return null; 825 } 826 827 return new File(dir, maxVersionFile); 828 } 829 static Map<String, File> nativeLibraryFiles = new HashMap<String, File>(); 830 831 /** 832 * Given a library name (e.g. "test"), finds the shared library file in the 833 * system-specific path ("/usr/bin/libtest.so", "./libtest.dylib", 834 * "c:\\windows\\system\\test.dll"...) 835 */ 836 public static File getNativeLibraryFile(String libraryName) { 837 if (libraryName == null) { 838 return null; 839 } 840 841 try { 842 synchronized (nativeLibraryFiles) { 843 File nativeLibraryFile = nativeLibraryFiles.get(libraryName); 844 if (debug) { 845 info("Library named '" + libraryName + "' is associated to file '" + nativeLibraryFiles + "'", null); 846 } 847 if (nativeLibraryFile == null) { 848 nativeLibraryFiles.put(libraryName, nativeLibraryFile = findNativeLibraryFile(libraryName)); 849 } 850 return nativeLibraryFile; 851 } 852 } catch (Throwable th) { 853 warning("Library not found : " + libraryName, debug ? th : null); 854 return null; 855 } 856 } 857 858 /** 859 * Associate a library name (e.g. "test"), to its shared library file. 860 */ 861 public static void setNativeLibraryFile(String libraryName, File nativeLibraryFile) { 862 if (libraryName == null) { 863 return; 864 } 865 866 synchronized (nativeLibraryFiles) { 867 nativeLibraryFiles.put(libraryName, nativeLibraryFile); 868 } 869 } 870 871 private static String getLibraryEnv(String libraryName) { 872 String env = getenv("BRIDJ_" + libraryName.toUpperCase() + "_LIBRARY"); 873 if (env == null) { 874 env = getProperty("bridj." + libraryName + ".library"); 875 } 876 return env; 877 } 878 879 private static String getDependenciesEnv(String libraryName) { 880 String env = getenv("BRIDJ_" + libraryName.toUpperCase() + "_DEPENDENCIES"); 881 if (env == null) { 882 env = getProperty("bridj." + libraryName + ".dependencies"); 883 } 884 return env; 885 } 886 887 static File findNativeLibraryFile(String libraryName) throws IOException { 888 //out.println("Getting file of '" + name + "'"); 889 String actualName = libraryActualNames.get(libraryName); 890 List<String> aliases = libraryAliases.get(libraryName); 891 List<String> possibleNames = new ArrayList<String>(); 892 if (Platform.isWindows()) { 893 if (libraryName.equals("c") || libraryName.equals("m")) { 894 possibleNames.add("msvcrt"); 895 } 896 } 897 if (aliases != null) { 898 possibleNames.addAll(aliases); 899 } 900 901 possibleNames.add(actualName == null ? libraryName : actualName); 902 903 //out.println("Possible names = " + possibleNames); 904 List<String> paths = getNativeLibraryPaths(); 905 if (debug) { 906 info("Looking for library '" + libraryName + "' " + (actualName != null ? "('" + actualName + "') " : "") + "in paths " + paths, null); 907 } 908 909 for (String name : possibleNames) { 910 String env = getLibraryEnv(name); 911 if (env != null) { 912 File f = new File(env); 913 if (f.exists()) { 914 try { 915 return f.getCanonicalFile(); 916 } catch (IOException ex) { 917 error(null, ex); 918 } 919 } 920 } 921 List<String> possibleFileNames = getPossibleFileNames(name); 922 if (debug) { 923 info("Possible file names for library '" + libraryName + "' with name '" + name + "': " + possibleFileNames, null); 924 } 925 926 for (String path : paths) { 927 File pathFile = path == null ? null : new File(path); 928 File f = new File(name); 929 if (!f.isFile() && pathFile != null) { 930 for (String possibleFileName : possibleFileNames) { 931 f = new File(pathFile, possibleFileName); 932 if (f.isFile()) { 933 break; 934 } 935 } 936 937 if (!f.isFile() && isLinux()) { 938 String[] files = pathFile.list(); 939 if (files != null) { 940 for (String possibleFileName : possibleFileNames) { 941 File ff = findFileWithGreaterVersion(pathFile, files, possibleFileName); 942 if (ff != null && (f = ff).isFile()) { 943 if (verbose) { 944 info("File '" + possibleFileName + "' was not found, used versioned file '" + f + "' instead."); 945 } 946 break; 947 } 948 } 949 } 950 } 951 } 952 953 if (!f.isFile()) { 954 continue; 955 } 956 957 try { 958 return f.getCanonicalFile(); 959 } catch (IOException ex) { 960 error(null, ex); 961 } 962 } 963 if (isMacOSX()) { 964 for (String s : new String[]{ 965 "/System/Library/Frameworks", 966 "/System/Library/Frameworks/ApplicationServices.framework/Frameworks", 967 new File(getProperty("user.home"), "Library/Frameworks").toString() 968 }) { 969 try { 970 File f = new File(new File(s, name + ".framework"), name); 971 if (f.isFile()) { 972 return f.getCanonicalFile(); 973 } 974 } catch (IOException ex) { 975 ex.printStackTrace(); 976 return null; 977 } 978 } 979 } 980 File f; 981 if (isAndroid()) { 982 f = new File("lib" + name + ".so"); 983 } else { 984 f = extractEmbeddedLibraryResource(name); 985 } 986 987 if (f != null && f.isFile()) { 988 return f; 989 } 990 } 991 throw new FileNotFoundException(StringUtils.implode(possibleNames, ", ")); 992 } 993 static Boolean directModeEnabled; 994 995 /** 996 * Query direct mode.<br> 997 * In direct mode, BridJ will <i>attempt</i> to optimize calls with 998 * assembler code, so that the overhead of each call is about the same as 999 * with plain JNI.<br> 1000 * Set -Dbridj.direct=false in the command line (or 1001 * setProperty("bridj.direct", "false")) or environment var BRIDJ_DIRECT=0 1002 * to disable 1003 */ 1004 public static boolean isDirectModeEnabled() { 1005 if (directModeEnabled == null) { 1006 directModeEnabled = 1007 Switch.Direct.enabled 1008 && !logCalls 1009 && !protectedMode; 1010 if (veryVerbose) { 1011 info("directModeEnabled = " + directModeEnabled); 1012 } 1013 } 1014 return directModeEnabled; 1015 } 1016 1017 /** 1018 * Set direct mode.<br> 1019 * In direct mode, BridJ will <i>attempt</i> to optimize calls with 1020 * assembler code, so that the overhead of each call is about the same as 1021 * with plain JNI.<br> 1022 * Set -Dbridj.direct=false in the command line (or 1023 * setProperty("bridj.direct", "false")) or environment var BRIDJ_DIRECT=0 1024 * to disable 1025 */ 1026 static void setDirectModeEnabled(boolean v) { 1027 directModeEnabled = v; 1028 } 1029 1030 /** 1031 * Loads the library with the name provided in argument (see 1032 * {@link #getNativeLibraryFile(String)}) 1033 */ 1034 public static synchronized NativeLibrary getNativeLibrary(String name) throws IOException { 1035 if (name == null) { 1036 return null; 1037 } 1038 1039 NativeLibrary l = libHandles.get(name); 1040 if (l != null) { 1041 return l; 1042 } 1043 1044 File f = getNativeLibraryFile(name); 1045 //if (f == null) { 1046 // throw new FileNotFoundException("Couldn't find library file for library '" + name + "'"); 1047 //} 1048 1049 return getNativeLibrary(name, f); 1050 } 1051 1052 /** 1053 * Loads the shared library file under the provided name. Any subsequent 1054 * call to {@link #getNativeLibrary(String)} will return this library. 1055 */ 1056 public static NativeLibrary getNativeLibrary(String name, File f) throws IOException { 1057 NativeLibrary ll = NativeLibrary.load(f == null ? name : f.toString());; 1058 if (ll == null) { 1059 ll = PlatformSupport.getInstance().loadNativeLibrary(name); 1060 if (ll == null) { 1061 boolean isWindows = Platform.isWindows(); 1062 if ("c".equals(name) || "m".equals(name) && isWindows) { 1063 ll = new NativeLibrary(isWindows ? "msvcrt" : null, 0, 0); 1064 } 1065 } 1066 } 1067 1068 //if (ll == null && f != null) 1069 // ll = NativeLibrary.load(f.getName()); 1070 if (ll == null) { 1071 if (f != null && f.exists()) { 1072 throw new RuntimeException("Library '" + name + "' was not loaded successfully from file '" + f + "'"); 1073 } else { 1074 throw new FileNotFoundException("Library '" + name + "' was not found in path '" + getNativeLibraryPaths() + "'" + (f != null && f.exists() ? " (failed to load " + f + ")" : "")); 1075 } 1076 } 1077 if (verbose) { 1078 info("Loaded library '" + name + "' from '" + f + "'", null); 1079 } 1080 1081 libHandles.put(name, ll); 1082 return ll; 1083 } 1084 1085 /** 1086 * Gets the name of the library declared for an annotated element. Recurses 1087 * up to parents of the element (class, enclosing classes) to find any 1088 * {@link org.bridj.ann.Library} annotation. 1089 */ 1090 public static String getNativeLibraryName(AnnotatedElement m) { 1091 Library lib = getLibrary(m); 1092 return lib == null ? null : lib.value(); 1093 } 1094 1095 /** 1096 * Gets the {@link org.bridj.ann.Library} annotation for an annotated 1097 * element. Recurses up to parents of the element (class, enclosing classes) 1098 * if needed 1099 */ 1100 static Library getLibrary(AnnotatedElement m) { 1101 return getInheritableAnnotation(Library.class, m); 1102 } 1103 1104 public static Symbol getSymbolByAddress(long peer) { 1105 for (NativeLibrary lib : libHandles.values()) { 1106 Symbol symbol = lib.getSymbol(peer); 1107 if (symbol != null) { 1108 return symbol; 1109 } 1110 } 1111 return null; 1112 } 1113 1114 public static void setOrphanEntities(NativeEntities orphanEntities) { 1115 BridJ.orphanEntities = orphanEntities; 1116 } 1117 1118 public static NativeEntities getOrphanEntities() { 1119 return orphanEntities; 1120 } 1121 1122 static void initialize(NativeObject instance) { 1123 Class<?> instanceClass = instance.getClass(); 1124 BridJRuntime runtime = getRuntime(instanceClass); 1125 Type type = runtime.getType(instance); 1126 TypeInfo typeInfo = getTypeInfo(runtime, type); 1127 instance.typeInfo = typeInfo; 1128 typeInfo.initialize(instance); 1129 } 1130 1131 static void initialize(NativeObject instance, Pointer peer, Object... targs) { 1132 Class<?> instanceClass = instance.getClass(); 1133 BridJRuntime runtime = getRuntime(instanceClass); 1134 Type type = runtime.getType(instanceClass, targs, null); 1135 TypeInfo typeInfo = getTypeInfo(runtime, type); 1136 instance.typeInfo = typeInfo; 1137 typeInfo.initialize(instance, peer); 1138 } 1139 1140 static void initialize(NativeObject instance, int constructorId, Object[] targsAndArgs) { 1141 Class<?> instanceClass = instance.getClass(); 1142 BridJRuntime runtime = getRuntime(instanceClass); 1143 int typeParamCount[] = new int[1]; 1144 Type type = runtime.getType(instanceClass, targsAndArgs, typeParamCount); 1145 int targsCount = typeParamCount[0]; 1146 Object[] args = takeRight(targsAndArgs, targsAndArgs.length - targsCount); 1147 // TODO handle template arguments here (or above), with class => ((class, args) => Type) caching 1148 TypeInfo typeInfo = getTypeInfo(runtime, type); 1149 instance.typeInfo = typeInfo; 1150 typeInfo.initialize(instance, constructorId, args); 1151 } 1152 1153 static <T extends NativeObject> T clone(T instance) throws CloneNotSupportedException { 1154 return ((TypeInfo<T>) instance.typeInfo).clone(instance); 1155 } 1156 1157 /** 1158 * Some native object need manual synchronization between Java fields and 1159 * native memory.<br> 1160 * An example is JNA-style structures. 1161 */ 1162 public static <T extends NativeObject> T readFromNative(T instance) { 1163 ((TypeInfo<T>) instance.typeInfo).readFromNative(instance); 1164 return instance; 1165 } 1166 1167 /** 1168 * Some native object need manual synchronization between Java fields and 1169 * native memory.<br> 1170 * An example is JNA-style structures. 1171 */ 1172 public static <T extends NativeObject> T writeToNative(T instance) { 1173 ((TypeInfo<T>) instance.typeInfo).writeToNative(instance); 1174 return instance; 1175 } 1176 1177 /** 1178 * Creates a string that describes the provided native object, printing 1179 * generally-relevant internal data (for instance for structures, this will 1180 * typically display the fields values).<br> 1181 * This is primarily useful for debugging purposes. 1182 */ 1183 public static String describe(NativeObject instance) { 1184 return ((TypeInfo) instance.typeInfo).describe(instance); 1185 } 1186 1187 /** 1188 * Creates a string that describes the provided native object type, printing 1189 * generally-relevant internal data (for instance for structures, this will 1190 * typically display name of the fields, their offsets and lengths...).<br> 1191 * This is primarily useful for debugging purposes. 1192 */ 1193 public static String describe(Type nativeObjectType) { 1194 BridJRuntime runtime = getRuntime(Utils.getClass(nativeObjectType)); 1195 TypeInfo typeInfo = getTypeInfo(runtime, nativeObjectType); 1196 return typeInfo == null ? Utils.toString(nativeObjectType) : typeInfo.describe(); 1197 } 1198 1199 public static void main(String[] args) { 1200 List<NativeLibrary> libraries = new ArrayList<NativeLibrary>(); 1201 try { 1202 File outputDir = new File("."); 1203 for (int iArg = 0, nArgs = args.length; iArg < nArgs; iArg++) { 1204 String arg = args[iArg]; 1205 if (arg.equals("-d")) { 1206 outputDir = new File(args[++iArg]); 1207 continue; 1208 } 1209 try { 1210 NativeLibrary lib = getNativeLibrary(arg); 1211 libraries.add(lib); 1212 1213 PrintWriter sout = new PrintWriter(new File(outputDir, new File(arg).getName() + ".symbols.txt")); 1214 for (Symbol sym : lib.getSymbols()) { 1215 sout.print(sym.getSymbol()); 1216 sout.print(" // "); 1217 try { 1218 MemberRef mr = sym.getParsedRef(); 1219 sout.print(" // " + mr); 1220 } catch (Throwable th) { 1221 sout.print("?"); 1222 } 1223 sout.println(); 1224 } 1225 sout.close(); 1226 } catch (Throwable th) { 1227 th.printStackTrace(); 1228 } 1229 } 1230 PrintWriter out = new PrintWriter(new File(outputDir, "out.h")); 1231 HeadersReconstructor.reconstructHeaders(libraries, out); 1232 out.close(); 1233 } catch (Exception ex) { 1234 ex.printStackTrace(); 1235 exit(1); 1236 } 1237 } 1238}