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