001    package org.bridj;
002    
003    import org.bridj.util.Utils;
004    import java.io.FileNotFoundException;
005    import java.io.IOException;
006    import java.lang.reflect.Constructor;
007    import java.lang.reflect.InvocationTargetException;
008    import java.lang.reflect.Method;
009    import java.lang.reflect.Modifier;
010    import java.util.HashSet;
011    import java.util.Map;
012    import java.util.Set;
013    import java.util.List;
014    import java.util.ArrayList;
015    
016    import org.bridj.demangling.Demangler.Symbol;
017    import org.bridj.NativeEntities.Builder;
018    import org.bridj.ann.Convention;
019    import org.bridj.ann.JNIBound;
020    import org.bridj.util.ConcurrentCache;
021    import static org.bridj.BridJ.*;
022    import static org.bridj.util.AnnotationUtils.*;
023    import java.lang.reflect.Type;
024    import java.util.Arrays;
025    import java.util.concurrent.atomic.AtomicReference;
026    import org.bridj.ann.Optional;
027    
028    /**
029     * C runtime (used by default when no {@link org.bridj.ann.Runtime} annotation is found).<br>
030     * Deals with registration and lifecycle of structs, functions, callbacks.<br>
031     * A shared C runtime instance can be retrieved with {@link CRuntime#getInstance() }.
032     * @author ochafik
033     */
034    public class CRuntime extends AbstractBridJRuntime {
035    
036            final static Set<Type> registeredTypes = new HashSet<Type>();
037            final AtomicReference<CallbackNativeImplementer> _callbackNativeImplementer = new AtomicReference<CallbackNativeImplementer>();
038    
039        /**
040         * @deprecated use {@link CRuntime#getInstance() } instead
041         */
042        @Deprecated
043        public CRuntime() {
044            
045        }
046    
047        public CallbackNativeImplementer getCallbackNativeImplementer() {
048            CallbackNativeImplementer impl = _callbackNativeImplementer.get();
049            if (impl == null) {
050                CallbackNativeImplementer newImpl = new CallbackNativeImplementer(BridJ.getOrphanEntities(), this);
051                if (_callbackNativeImplementer.compareAndSet(null, newImpl))
052                    impl = newImpl;
053                else
054                    impl = _callbackNativeImplementer.get();
055            }
056            return impl;
057        }
058        
059        
060        public boolean isAvailable() {
061            return true;
062        }
063    
064        public static CRuntime getInstance() {
065            return BridJ.getRuntimeByRuntimeClass(CRuntime.class);
066        }
067        
068        
069            //@Override
070            public <T extends NativeObject> Class<? extends T> getActualInstanceClass(Pointer<T> pInstance, Type officialType) {
071                    return Utils.getClass(officialType);
072            }
073    
074        public class CTypeInfo<T extends NativeObject> implements TypeInfo<T> {
075            public CTypeInfo(Type type) {
076                this.type = type;
077                this.typeClass = Utils.getClass(type);
078                this.structIO = StructIO.getInstance(typeClass, typeClass);
079                if (structIO != null)
080                            structIO.build();
081                this.pointerIO = (PointerIO<T>)PointerIO.getInstance(structIO);
082                //this.castClass = getTypeForCast(typeClass);
083                register(typeClass);
084            }
085            protected final Type type;
086            protected final Class<T> typeClass;
087                    protected final StructIO structIO;
088                    protected final PointerIO<T> pointerIO;
089            protected volatile Class<T> castClass;
090    
091            //@Override
092            public long sizeOf() {
093                return structIO.getStructSize();
094            }
095                    //@Override
096            public boolean equal(T instance, T other) {
097                            if (structIO != null) {
098                                    if (((StructObject)instance).io != structIO)
099                                            throw new IllegalArgumentException("This is not this instance's StructIO");
100                                    
101                                    if (((StructObject)other).io != structIO)
102                                            return false;
103                                    
104                                    return structIO.equal((StructObject)instance, (StructObject)other);
105                            } else {
106                                    return instance.peer.equals(other.peer);        
107                            }
108            }
109            //@Override
110            public int compare(T instance, T other) {
111                            if (structIO != null) {
112                                    if (((StructObject)instance).io != structIO)
113                                            throw new IllegalArgumentException("This is not this instance's StructIO");
114                                    
115                                    if (((StructObject)other).io != structIO)
116                                            return 1;
117                                    
118                                    return structIO.compare((StructObject)instance, (StructObject)other);
119                            } else {
120                                    return instance.peer.compareTo(other.peer);     
121                            }
122            }
123                    
124            
125            //@Override
126            public BridJRuntime getRuntime() {
127                return CRuntime.this;
128            }
129            //@Override
130            public Type getType() {
131                return type;
132            }
133            
134            protected Class<T> getCastClass() {
135                if (castClass == null)
136                    castClass = (Class<T>)getTypeForCast(typeClass);
137                return castClass;
138            }
139            protected T newCastInstance() throws NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
140                Class<?> cc = getCastClass();
141                try {
142                    return (T)cc.newInstance();
143                } catch (IllegalAccessException ex) {
144                    Constructor<T> constructor = (Constructor<T>) cc.getConstructor();
145                    constructor.setAccessible(true);
146                    return constructor.newInstance();
147                }
148            }
149    
150            //@Override
151            public T cast(Pointer peer) {
152                try {
153                    T instance = newCastInstance();
154                    // TODO template parameters here !!!
155                    initialize(instance, peer);
156                    return instance;
157                } catch (Exception ex) {
158                    throw new RuntimeException("Failed to cast pointer " + peer + " to instance of type " + Utils.toString(type), ex);
159                }
160            }
161            //@Override
162            public T createReturnInstance() {
163                try {
164                    T instance = newCastInstance();
165                    initialize(instance);
166                    return instance;
167                } catch (Exception ex) {
168                    throw new RuntimeException("Failed to create return instance for type " + Utils.toString(type), ex);
169                }
170            }
171            //@Override
172            public void writeToNative(T instance) {
173                            if (instance instanceof StructObject)
174                                    structIO.writeFieldsToNative((StructObject)instance);
175            }
176            //@Override
177            public void readFromNative(T instance) {
178                            if (instance instanceof StructObject)
179                                    structIO.readFieldsFromNative((StructObject)instance);
180            }
181            public void copyNativeObjectToAddress(T instance, Pointer<T> ptr) {
182                            if (instance instanceof StructObject) {
183                                    // TODO override in C++ to call operator=
184                                    ((StructObject)instance).peer.copyBytesTo(ptr, structIO.getStructSize());
185                            }
186            }
187            //@Override
188            public String describe(T instance) {
189                            if (instance instanceof StructObject)
190                                    return structIO.describe((StructObject)instance);
191                            else
192                                    return instance.toString();
193            }
194            //@Override
195            public String describe() {
196                            if (structIO != null)
197                                    return structIO.describe();
198                            else
199                                    return Utils.toString(typeClass);
200            }
201    
202            //@Override
203            public void initialize(T instance) {
204                if (!BridJ.isCastingNativeObjectInCurrentThread()) {
205                    if (instance instanceof CallbackInterface) {
206                        if (!(instance instanceof DynamicFunction))
207                            setNativeObjectPeer(instance, registerCallbackInstance((CallbackInterface)instance));
208                    } else
209                        initialize(instance, -1);
210    
211                    if (instance instanceof StructObject)
212                        structIO.readFieldsFromNative((StructObject)instance);
213                } else if (instance instanceof StructObject) {
214                    ((StructObject)instance).io = structIO;
215                }
216            }
217            //@Override
218            public void initialize(T instance, Pointer peer) {
219                instance.peer = peer;
220                if (instance instanceof StructObject) {
221                    ((StructObject)instance).io = structIO;
222                    structIO.readFieldsFromNative((StructObject)instance);
223                }
224            }
225    
226            //@Override
227            public void initialize(T instance, int constructorId, Object... args) {
228                StructObject s = (StructObject)instance;
229                if (constructorId < 0) {
230                    s.io = structIO;
231                    if (instance.peer == null)
232                        instance.peer = Pointer.allocate(pointerIO);
233                } else
234                    throw new UnsupportedOperationException("TODO implement structs constructors !");
235            }
236    
237            //@Override
238            public T clone(T instance) throws CloneNotSupportedException {
239                if (instance == null)
240                    return null;
241    
242                try {
243                    T clone = (T)typeClass.newInstance();
244                    Pointer<T> p = Pointer.allocate(pointerIO);
245                    Pointer.pointerTo(instance).copyTo(p);
246                    initialize(clone, p);
247                    return clone;
248                } catch (Exception ex) {
249                    throw new RuntimeException("Failed to clone instance of type " + getType());
250                }
251            }
252            
253            //@Override
254            public void destroy(T instance) {
255                if (instance instanceof CallbackInterface)
256                    return;        
257            }
258        }
259        /// Needs not be fast : TypeInfo will be cached in BridJ anyway !
260            //@Override
261            public <T extends NativeObject> TypeInfo<T> getTypeInfo(final Type type) {
262            return new CTypeInfo(type);
263            }
264            //@Override
265            public void register(Type type) {
266            register(type, null, null);
267        }
268        public static class MethodCallInfoBuilder {
269            public MethodCallInfo apply(Method method) throws FileNotFoundException {
270                            return new MethodCallInfo(method);
271                    }
272        }
273            @Override
274            public void unregister(Type type) {
275                    Class typeClass = Utils.getClass(type);
276            registeredTypes.remove(typeClass);
277            }
278            synchronized void register(Type type, NativeLibrary forcedLibrary, MethodCallInfoBuilder methodCallInfoBuilder) {
279            Class typeClass = Utils.getClass(type);
280            if (!BridJ.getRuntimeClass(typeClass).isInstance(this)) {
281                BridJ.register(typeClass);
282                return;
283            }
284            synchronized (registeredTypes) {
285                if (!registeredTypes.add(typeClass))
286                                return;
287            }
288    
289                    if (methodCallInfoBuilder == null)
290                            methodCallInfoBuilder = new MethodCallInfoBuilder();
291                    
292            assert info("Registering type " + Utils.toString(type));
293            
294                    int typeModifiers = typeClass.getModifiers();
295                    
296                    NativeLibrary typeLibrary = null;
297                    try {
298                            typeLibrary = forcedLibrary == null ? getNativeLibrary(typeClass) : forcedLibrary;
299                    } catch (Throwable th) {}
300                    
301                    ConcurrentCache<NativeEntities, NativeEntities.Builder> builders = new ConcurrentCache<NativeEntities, NativeEntities.Builder>(NativeEntities.Builder.class);
302                    try {
303                Set<Method> handledMethods = new HashSet<Method>();
304                            /*if (StructObject.class.isAssignableFrom(typeClass)) {
305                                    StructIO io = StructIO.getInstance(typeClass, type, this); // TODO handle differently with templates...
306                    io.build();
307                    StructIO.FieldIO[] fios = io == null ? null : io.getFields();
308                    if (fios != null)
309                        for (StructIO.FieldIO fio : fios) {
310                            NativeEntities.Builder builder = builders.get(BridJ.getOrphanEntities());
311    
312                            try {
313                                {
314                                    MethodCallInfo getter = new MethodCallInfo(fio.getter);
315                                    getter.setIndex(fio.index);
316                                    builder.addGetter(getter);
317                                    handledMethods.add(fio.getter);
318                                }
319                                if (fio.setter != null) {
320                                    MethodCallInfo setter = new MethodCallInfo(fio.setter);
321                                    setter.setIndex(fio.index);
322                                    builder.addSetter(setter);
323                                    handledMethods.add(fio.setter);
324                                }
325                            } catch (Exception ex) {
326                                System.err.println("Failed to register field " + fio.name + " in struct " + type);
327                                ex.printStackTrace();
328                            }
329                        }
330                            }*/
331                            
332                            if (CallbackInterface.class.isAssignableFrom(typeClass)) {
333                                    if (rootCallbackClasses.contains(type))
334                                            return;
335                                    
336                                    if (Modifier.isAbstract(typeModifiers))
337                            getCallbackNativeImplementer().getCallbackImplType((Class) type, forcedLibrary);
338                            }
339                    
340                    
341    //              for (; type != null && type != Object.class; type = type.getSuperclass()) {
342                            List<Method> nativeMethods = new ArrayList<Method>();
343                            for (Method method : typeClass.getDeclaredMethods()) {
344                                    int modifiers = method.getModifiers();
345                                    if (Modifier.isNative(modifiers)) {
346                                            if (!isAnnotationPresent(JNIBound.class, method))
347                                                    nativeMethods.add(method);
348                                    }
349                            }
350                                            
351                            if (!nativeMethods.isEmpty())
352                            try {
353                                    for (Method method : nativeMethods) {
354                        if (!handledMethods.add(method))
355                            continue;
356                                            
357                                            try {
358                                                    NativeLibrary methodLibrary = forcedLibrary == null ? BridJ.getNativeLibrary(method) : forcedLibrary;
359                            NativeEntities nativeEntities = methodLibrary == null ? BridJ.getOrphanEntities() : methodLibrary.getNativeEntities();
360                            NativeEntities.Builder builder = builders.get(nativeEntities);
361                                                    
362                            registerNativeMethod(typeClass, typeLibrary, method, methodLibrary, builder, methodCallInfoBuilder);
363                                            } catch (Exception ex) {
364                                                    assert error("Method " + method.toGenericString() + " cannot be mapped : " + ex, ex);
365                                            }
366                                    }
367                            } catch (Exception ex) {
368                                    throw new RuntimeException("Failed to register class " + Utils.toString(type), ex);
369                            }
370    //              }
371                    } finally {
372                            for (Map.Entry<NativeEntities, NativeEntities.Builder> e : builders.entrySet()) {
373                                    e.getKey().addDefinitions(typeClass, e.getValue());
374                            }
375                            registerFamily(type, forcedLibrary, methodCallInfoBuilder);
376                    }
377            }
378            protected void registerFamily(Type type, NativeLibrary forcedLibrary, MethodCallInfoBuilder methodCallInfoBuilder) {
379                    Class typeClass = Utils.getClass(type);
380            
381                    for (Class<?> child : typeClass.getClasses())
382                            register(child, forcedLibrary, methodCallInfoBuilder);
383                    
384                    typeClass = typeClass.getSuperclass();
385                    if (typeClass != null && typeClass != Object.class)
386                            register(typeClass, forcedLibrary, methodCallInfoBuilder);
387            }
388    
389            protected NativeLibrary getNativeLibrary(Class<?> type) throws IOException {
390                    return BridJ.getNativeLibrary(type);
391            }
392            protected boolean isSymbolOptional(Method method) {
393                    return getInheritableAnnotation(Optional.class, method) != null;
394            }
395            protected void registerNativeMethod(
396                            Class<?> type, 
397                            NativeLibrary typeLibrary, 
398                            Method method, 
399                            NativeLibrary methodLibrary, 
400                            Builder builder, 
401                            MethodCallInfoBuilder methodCallInfoBuilder
402                    ) throws FileNotFoundException 
403            {
404                    MethodCallInfo mci;
405                    try {
406                            mci = methodCallInfoBuilder.apply(method);
407                            if (mci == null)
408                                    return;
409                            //System.out.println("method.dcCallingConvention = " + mci.dcCallingConvention + " (for method " + type.getName() + ", method " + method + ", type = " + type.getName() + ", enclosingClass = " + method.getDeclaringClass().getName() + ")");
410                    } catch (Throwable th) {
411                            error("Unable to register " + method + " : " + th);
412                th.printStackTrace();
413                            return;
414                    }
415                    if (CallbackInterface.class.isAssignableFrom(type)) {
416                if (debug)
417                                    info("Registering java -> native callback : " + method);
418                builder.addJavaToNativeCallback(mci);
419            } else {
420                Symbol symbol = methodLibrary == null ? null : methodLibrary.getSymbol(method);
421                if (symbol == null)
422                {
423    //                for (Demangler.Symbol symbol : methodLibrary.getSymbols()) {
424    //                    if (symbol.matches(method)) {
425    //                        address = symbol.getAddress();
426    //                        break;
427    //                    }
428    //                }
429    //                if (address == null) {
430                    if (!isSymbolOptional(method))
431                        error("Failed to get address of method " + method);
432                        return;
433    //                }
434                }
435                mci.setForwardedPointer(symbol.getAddress());
436                            if (!mci.hasCallingConvention()) {
437                                    Convention.Style cc = symbol.getInferredCallingConvention();
438                                    if (cc != null)
439                                            mci.setCallingConvention(cc);
440                            }                       
441                            builder.addFunction(mci);
442                if (debug)
443                                    info("Registering " + method + " as C function " + symbol.getName());
444            }
445            }
446            
447            public <T extends NativeObject> Pointer<T> allocate(Class<T> type, int constructorId, Object... args) {
448                if (CallbackInterface.class.isAssignableFrom(type)) {
449                    if (constructorId != -1 || args.length != 0)
450                            throw new RuntimeException("Callback should have a constructorId == -1 and no constructor args !");
451                    return null;//newCallbackInstance(type);
452            }
453            throw new RuntimeException("Cannot allocate instance of type " + type.getName() + " (unhandled NativeObject subclass)");
454            }
455            
456            static final int defaultObjectSize = Platform.is64Bits() ? 8 : 4;
457            public static final String PROPERTY_bridj_c_defaultObjectSize = "bridj.c.defaultObjectSize";
458            
459            public int getDefaultStructSize() {
460                    String s = System.getProperty(PROPERTY_bridj_c_defaultObjectSize);
461            if (s != null)
462                    try {
463                            return Integer.parseInt(s);
464                    } catch (Throwable th) {
465                            error("Invalid value for property " + PROPERTY_bridj_c_defaultObjectSize + " : '" + s + "'");
466                    }
467            return defaultObjectSize;
468            }
469            public long sizeOf(Type structType, StructIO io) {
470                    if (io == null)
471                            io = StructIO.getInstance(Utils.getClass(structType), structType);
472                    long size;
473                    if (io == null || (size = io.getStructSize()) <= 0)
474                            return getDefaultStructSize();
475                    return size;    
476        }
477    
478        protected Set<Class> rootCallbackClasses = new HashSet<Class>(Arrays.asList(Callback.class, DynamicFunction.class));
479        
480        protected Method getUniqueAbstractCallbackMethod(Class type) {
481            Class<?> parent = null;
482            while ((parent = type.getSuperclass()) != null && !rootCallbackClasses.contains(parent)) {
483                    type = parent;
484            }
485    
486            Method method = null;
487            for (Method dm : type.getDeclaredMethods()) {
488                    int modifiers = dm.getModifiers();
489                    if (!Modifier.isAbstract(modifiers))
490                            continue;
491    
492                    if (method == null)
493                            method = dm;
494                    else
495                            throw new RuntimeException("Callback " + type.getName() + " has more than one abstract method (" + dm + " and " + method + ")");
496                    //break;
497            }
498            if (method == null)
499                    throw new RuntimeException("Type doesn't have any abstract method : " + type.getName() + " (parent = " + parent.getName() + ")");
500            return method;
501        }
502    
503        public <T extends NativeObject> Class<? extends T> getTypeForCast(Type type) {
504            Class<?> typeClass = Utils.getClass(type);
505            if (CallbackInterface.class.isAssignableFrom(typeClass))
506                return getCallbackNativeImplementer().getCallbackImplType((Class) typeClass, null);
507            else
508                return (Class<? extends T>)typeClass;
509        }
510    
511        /**
512         * Get a shared factory of native function wrappers that have a given signatures.
513         * @param library library to which the allocated native thunks will be bound (can be null, in which case the native allocations will be bound to {@link BridJ#getOrphanEntities() })
514         * @param callingConvention calling convention used by the functions (if null, default is typically {@link org.bridj.ann.Convention.Style#CDecl})
515         * @param returnType return type of the functions
516         * @param parameterTypes parameter types of the functions
517         * Also see {@link DynamicFunction} and {@link Pointer#asDynamicFunction(org.bridj.ann.Convention.Style, java.lang.reflect.Type, java.lang.reflect.Type[]) }.
518         */
519        public DynamicFunctionFactory getDynamicFunctionFactory(NativeLibrary library, Convention.Style callingConvention, Type returnType, Type... parameterTypes) {
520            return getCallbackNativeImplementer().getDynamicCallback(library, callingConvention, returnType, parameterTypes);
521        }
522    
523        public static <T> Pointer<T> createCToJavaCallback(MethodCallInfo mci, Type t) {
524            mci.prependCallbackCC();
525            final long handle = JNI.createCToJavaCallback(mci);
526                    long peer = JNI.getActualCToJavaCallback(handle);
527            
528            final Throwable stackTrace = BridJ.debugPointers ?
529                            new RuntimeException().fillInStackTrace() : null;
530            
531                    return (Pointer)Pointer.pointerToAddress(peer, t, new Pointer.Releaser() {
532    
533                            //@Override
534                            public void release(Pointer<?> p) {
535                    if (BridJ.debugPointers)
536                            BridJ.info("Freeing callback pointer " + p + "\n(Creation trace = \n\t" + Utils.toString(p.creationTrace).replaceAll("\n", "\n\t") + "\n)", new RuntimeException().fillInStackTrace());
537                    
538                                    if (BridJ.debugNeverFree)
539                                            return;
540                                    
541                                    JNI.freeCToJavaCallback(handle);//p.getPeer());
542                            }
543                    });
544        }
545        protected <T extends CallbackInterface> Pointer<T> registerCallbackInstance(T instance) {
546                    try {
547                Class<?> c = instance.getClass();
548                MethodCallInfo mci = new MethodCallInfo(getUniqueAbstractCallbackMethod(c));
549                mci.setDeclaringClass(c);
550                mci.setJavaCallback(instance);
551                return createCToJavaCallback(mci, c);
552                    } catch (Exception e) {
553                            throw new RuntimeException("Failed to register callback instance of type " + instance.getClass().getName(), e);
554                    }
555            }
556    
557        protected void setNativeObjectPeer(NativeObjectInterface instance, Pointer<? extends NativeObjectInterface> peer) {
558            ((NativeObject)instance).peer = (Pointer<NativeObject>)peer;
559        }
560    }