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