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