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 }