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    }