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}