001 package org.bridj;
002 import java.lang.annotation.Annotation;
003 import static org.bridj.NativeConstants.*;
004 import static org.bridj.dyncall.DyncallLibrary.*;
005 import org.bridj.ann.Constructor;
006 //import org.bridj.cpp.CPPObject;
007
008 import java.lang.reflect.*;
009 import java.util.ArrayList;
010 import java.util.Arrays;
011 import java.util.List;
012 import java.util.NoSuchElementException;
013 import org.bridj.ann.Convention;
014 import org.bridj.ann.DisableDirect;
015 import org.bridj.ann.Ptr;
016 import org.bridj.ann.Virtual;
017 import org.bridj.util.Utils;
018 import static org.bridj.util.AnnotationUtils.*;
019 /**
020 * Internal class that encapsulate all the knowledge about a native method call : signatures (ASM, dyncall and Java), calling convention, context...
021 * @author Olivier
022 */
023 public class MethodCallInfo {
024
025 /*public static class GenericMethodInfo {
026 Type returnType, paramsTypes[];
027 }
028 GenericMethodInfo genericInfo = new GenericMethodInfo();*/
029 List<CallIO> callIOs;
030 private Class<?> declaringClass;
031 long nativeClass;
032 int returnValueType, paramsValueTypes[];
033 Method method;//, definition;
034 String methodName, symbolName;
035 private long forwardedPointer;
036 String dcSignature;
037 String javaSignature;
038 String asmSignature;
039 Object javaCallback;
040 boolean isGenericCallback;
041 boolean isObjCBlock;
042 int virtualIndex = -1;
043 int virtualTableOffset = 0;
044 private int dcCallingConvention = DC_CALL_C_DEFAULT;
045
046 boolean isVarArgs;
047 boolean isStatic;
048 boolean isCPlusPlus;
049 boolean direct;
050 boolean startsWithThis;
051 boolean bNeedsThisPointer;
052 boolean bThrowLastError;
053
054 public MethodCallInfo(Method method) {
055 this(method, method);
056 }
057 static boolean derivesFrom(Class<?> c, String className) {
058 while (c != null) {
059 if (c.getName().equals(className))
060 return true;
061 c = c.getSuperclass();
062 }
063 return false;
064 }
065 public MethodCallInfo(Type genericReturnType, Type[] parameterTypes, boolean prependJNIPointers) {
066 this(genericReturnType, new Annotation[0], parameterTypes, new Annotation[parameterTypes.length][], prependJNIPointers);
067 }
068 public MethodCallInfo(Type genericReturnType, Annotation[] returnAnnotations, Type[] parameterTypes, Annotation[][] paramsAnnotations, boolean prependJNIPointers) {
069 init(null, Utils.getClass(genericReturnType), genericReturnType, returnAnnotations, Utils.getClasses(parameterTypes), parameterTypes, paramsAnnotations, prependJNIPointers, false, true);
070 }
071 public MethodCallInfo(Method method, Method definition) {
072 this.setMethod(method);
073 //this.setDefinition(definition);
074 this.setDeclaringClass(method.getDeclaringClass());
075 symbolName = methodName;
076
077 int modifiers = method.getModifiers();
078 isStatic = Modifier.isStatic(modifiers);
079 isVarArgs = method.isVarArgs();
080 boolean isNative = Modifier.isNative(modifiers);
081 boolean isVirtual = isAnnotationPresent(Virtual.class, definition);
082 boolean isDirectModeAllowed =
083 getInheritableAnnotation(DisableDirect.class, definition) == null &&
084 BridJ.isDirectModeEnabled();
085
086 isCPlusPlus = !isStatic && derivesFrom(method.getDeclaringClass(), "org.bridj.cpp.CPPObject");
087 isObjCBlock = !isStatic && derivesFrom(method.getDeclaringClass(), "org.bridj.objc.ObjCBlock");
088
089 init(
090 method,
091 method.getReturnType(), method.getGenericReturnType(), method.getAnnotations(),
092 method.getParameterTypes(), method.getGenericParameterTypes(), method.getParameterAnnotations(),
093 isNative,
094 isVirtual,
095 isDirectModeAllowed
096 );
097
098 Convention cc = getInheritableAnnotation(Convention.class, definition);
099 if (cc != null) {
100 setCallingConvention(cc.value());
101 }
102 List<Class<?>> exceptionTypes = Arrays.asList(definition.getExceptionTypes());
103 if (!exceptionTypes.isEmpty()) {
104 this.direct = false; // there is no crash / exception protection for direct raw calls
105 if (exceptionTypes.contains(LastError.class))
106 this.bThrowLastError = true;
107 }
108
109 }
110 protected void init(AnnotatedElement annotatedElement, Class returnType, Type genericReturnType, Annotation[] returnAnnotations, Class[] parameterTypes, Type[] genericParameterTypes, Annotation[][] paramsAnnotations, boolean prependJNIPointers, boolean isVirtual, boolean isDirectModeAllowed) {
111 assert returnType != null;
112 assert genericReturnType != null;
113 assert parameterTypes != null;
114 assert genericParameterTypes != null;
115 assert returnAnnotations != null;
116 assert parameterTypes.length == genericParameterTypes.length;
117 assert paramsAnnotations.length == genericParameterTypes.length;
118
119 int nParams = genericParameterTypes.length;
120 paramsValueTypes = new int[nParams];
121
122 direct = isDirectModeAllowed; // TODO on native side : test number of parameters (on 64 bits win : must be <= 4)
123
124 StringBuilder
125 javaSig = new StringBuilder(64),
126 asmSig = new StringBuilder(64),
127 dcSig = new StringBuilder(16);
128 javaSig.append('(');
129 asmSig.append('(');
130 if (prependJNIPointers)//!isCPlusPlus)
131 dcSig.append(DC_SIGCHAR_POINTER).append(DC_SIGCHAR_POINTER); // JNIEnv*, jobject: always present in native-bound functions
132
133 if (BridJ.debug)
134 BridJ.info("Analyzing " + (declaringClass == null ? "anonymous method" : declaringClass.getName() + "." + methodName));
135
136 if (isObjCBlock)
137 appendToSignature(0, ValueType.ePointerValue, Pointer.class, Pointer.class, null, dcSig, null);
138
139 for (int iParam = 0; iParam < nParams; iParam++) {
140 // Options paramOptions = paramsOptions[iParam] = new Options();
141 Type genericParameterType = genericParameterTypes[iParam];
142 Class<?> parameterType = parameterTypes[iParam];
143
144 ValueType paramValueType = getValueType(iParam, nParams, parameterType, genericParameterType, null, paramsAnnotations[iParam]);
145 if (BridJ.veryVerbose)
146 BridJ.info("\tparam " + paramValueType);
147 paramsValueTypes[iParam] = paramValueType.ordinal();
148
149 appendToSignature(iParam, paramValueType, parameterType, genericParameterType, javaSig, dcSig, asmSig);
150 }
151 javaSig.append(')');
152 asmSig.append(')');
153 dcSig.append(')');
154
155 ValueType retType = getValueType(-1, nParams, returnType, genericReturnType, annotatedElement, returnAnnotations);
156 if (BridJ.veryVerbose)
157 BridJ.info("\treturns " + retType);
158 appendToSignature(-1, retType, returnType, genericReturnType, javaSig, dcSig, asmSig);
159 returnValueType = retType.ordinal();
160
161 javaSignature = javaSig.toString();
162 asmSignature = asmSig.toString();
163 dcSignature = dcSig.toString();
164
165 isCPlusPlus = isCPlusPlus || isVirtual;
166
167 if (isCPlusPlus && !isStatic) {
168 if (!startsWithThis)
169 direct = false;
170 bNeedsThisPointer = true;
171 if (Platform.isWindows()) {
172 if (!Platform.is64Bits())
173 setDcCallingConvention(DC_CALL_C_X86_WIN32_THIS_MS);
174 } else {
175 //if (!Platform.is64Bits())
176 // setDcCallingConvention(DC_CALL_C_X86_WIN32_THIS_GNU);
177 }
178 }
179
180 if (nParams > Platform.getMaxDirectMappingArgCount())
181 this.direct = false;
182
183 if (BridJ.veryVerbose) {
184 BridJ.info("\t-> direct " + direct);
185 BridJ.info("\t-> javaSignature " + javaSignature);
186 BridJ.info("\t-> callIOs " + callIOs);
187 BridJ.info("\t-> asmSignature " + asmSignature);
188 BridJ.info("\t-> dcSignature " + dcSignature);
189 }
190
191 if (BridJ.veryVerbose)
192 BridJ.info((direct ? "[mappable as direct] " : "[not mappable as direct] ") + method);
193 }
194
195 boolean hasCC;
196 public boolean hasCallingConvention() {
197 return hasCC;
198 }
199 public void setCallingConvention(Convention.Style style) {
200 if (style == null)
201 return;
202
203 if (!Platform.isWindows() || Platform.is64Bits())
204 return;
205
206 switch (style) {
207 case FastCall:
208 this.direct = false;
209 setDcCallingConvention(Platform.isWindows() ? DC_CALL_C_X86_WIN32_FAST_MS : DC_CALL_C_DEFAULT); // TODO allow GCC-compiled C++ libs on windows
210 break;
211 case Pascal:
212 case StdCall:
213 this.direct = false;
214 setDcCallingConvention(DC_CALL_C_X86_WIN32_STD);
215 break;
216 case ThisCall:
217 this.direct = false;
218 setDcCallingConvention(Platform.isWindows() ? DC_CALL_C_X86_WIN32_THIS_MS : DC_CALL_C_DEFAULT);
219 }
220 if (BridJ.veryVerbose)
221 BridJ.info("Setting CC " + style + " (-> " + dcCallingConvention + ") for " + methodName);
222
223 }
224 void addCallIO(CallIO handler) {
225 if (callIOs == null)
226 callIOs = new ArrayList<CallIO>();
227 callIOs.add(handler);
228 }
229 public CallIO[] getCallIOs() {
230 if (callIOs == null)
231 return new CallIO[0];
232 return callIOs.toArray(new CallIO[callIOs.size()]);
233 }
234
235 public void prependCallbackCC() {
236 char cc = getDcCallbackConvention(getDcCallingConvention());
237 if (cc == 0)
238 return;
239
240 dcSignature = String.valueOf(DC_SIGCHAR_CC_PREFIX) + String.valueOf(cc) + dcSignature;
241 }
242 public String getDcSignature() {
243 return dcSignature;
244 }
245 public String getJavaSignature() {
246 return javaSignature;
247 }
248 public String getASMSignature() {
249 return asmSignature;
250 }
251 boolean getBoolAnnotation(Class<? extends Annotation> ac, AnnotatedElement element, Annotation... directAnnotations) {
252 Annotation ann = getAnnotation(ac, element, directAnnotations);
253 return ann != null;
254 }
255 public ValueType getValueType(int iParam, int nParams, Class<?> c, Type t, AnnotatedElement element, Annotation... directAnnotations) {
256 boolean isPtr = isAnnotationPresent(Ptr.class, element, directAnnotations);
257 boolean isCLong = isAnnotationPresent(org.bridj.ann.CLong.class, element, directAnnotations);
258 Constructor cons = getAnnotation(Constructor.class, element, directAnnotations);
259
260 if (isPtr || cons != null || isCLong) {
261 if (!(c == Long.class || c == Long.TYPE))
262 throw new RuntimeException("Annotation should only be used on a long parameter, not on a " + c.getName());
263
264 if (isPtr) {
265 if (!Platform.is64Bits())
266 direct = false;
267 } else if (isCLong) {
268 if (Platform.CLONG_SIZE != 8)
269 direct = false;
270 } else if (cons != null) {
271 isCPlusPlus = true;
272 startsWithThis = true;
273 if (iParam != 0)
274 throw new RuntimeException("Annotation " + Constructor.class.getName() + " cannot have more than one (long) argument");
275 }
276 return ValueType.eSizeTValue;
277 }
278 if (c == null || c.equals(Void.TYPE))
279 return ValueType.eVoidValue;
280 if (c == Integer.class || c == Integer.TYPE)
281 return ValueType.eIntValue;
282 if (c == Long.class || c == Long.TYPE) {
283 return !isPtr || Platform.is64Bits() ? ValueType.eLongValue : ValueType.eIntValue;
284 }
285 if (c == Short.class || c == Short.TYPE)
286 return ValueType.eShortValue;
287 if (c == Byte.class || c == Byte.TYPE)
288 return ValueType.eByteValue;
289 if (c == Boolean.class || c == Boolean.TYPE)
290 return ValueType.eBooleanValue;
291 if (c == Float.class || c == Float.TYPE) {
292 usesFloats();
293 return ValueType.eFloatValue;
294 }
295 if (c == char.class || c == Character.TYPE) {
296 if (Platform.WCHAR_T_SIZE != 2)
297 direct = false;
298 return ValueType.eWCharValue;
299 }
300 if (c == Double.class || c == Double.TYPE) {
301 usesFloats();
302 return ValueType.eDoubleValue;
303 }
304 if (c == CLong.class) {
305 direct = false;
306 return ValueType.eCLongObjectValue;
307 }
308 if (c == SizeT.class) {
309 direct = false;
310 return ValueType.eSizeTObjectValue;
311 }
312 if (c == TimeT.class) {
313 direct = false;
314 return ValueType.eTimeTObjectValue;
315 }
316 if (Pointer.class.isAssignableFrom(c)) {
317 direct = false;
318 CallIO cio = CallIO.Utils.createPointerCallIO(c, t);
319 if (BridJ.veryVerbose)
320 BridJ.info("CallIO : " + cio);
321 addCallIO(cio);
322 return ValueType.ePointerValue;
323 }
324 if (c.isArray() && iParam == nParams - 1) {
325 direct = false;
326 return ValueType.eEllipsis;
327 }
328 if (ValuedEnum.class.isAssignableFrom(c)) {
329 direct = false;
330 CallIO cio = CallIO.Utils.createValuedEnumCallIO((Class)Utils.getClass(Utils.getUniqueParameterizedTypeParameter(t)));
331 if (BridJ.veryVerbose)
332 BridJ.info("CallIO : " + cio);
333 addCallIO(cio);
334
335 return ValueType.eIntFlagSet;
336 }
337 if (NativeObject.class.isAssignableFrom(c)) {
338 Pointer<DCstruct> pStruct = null;
339 if (StructObject.class.isAssignableFrom(c)) {
340 StructIO io = StructIO.getInstance(c, t);
341 try {
342 pStruct = DyncallStructs.buildDCstruct(io);
343 } catch (Throwable th) {
344 BridJ.error("Unable to create low-level struct metadata for " + Utils.toString(t) + " : won't be able to use it as a by-value function argument.", th);
345 }
346 }
347 addCallIO(new CallIO.NativeObjectHandler((Class<? extends NativeObject>)c, t, pStruct));
348 direct = false;
349 return ValueType.eNativeObjectValue;
350 }
351
352 throw new NoSuchElementException("No " + ValueType.class.getSimpleName() + " for class " + c.getName());
353 }
354 void usesFloats() {
355 /*
356 if (direct && Platform.isMacOSX()) {
357 direct = false;
358 assert BridJ.warning("[unstable direct] FIXME Disable direct call due to float/double usage in " + method);
359 }
360 */
361 }
362
363 public void appendToSignature(int iParam, ValueType type, Class<?> parameterType, Type genericParameterType, StringBuilder javaSig, StringBuilder dcSig, StringBuilder asmSig) {
364 char dcChar;
365 String javaChar, asmChar = null;
366 switch (type) {
367 case eVoidValue:
368 dcChar = DC_SIGCHAR_VOID;
369 javaChar = "V";
370 break;
371 case eIntValue:
372 dcChar = DC_SIGCHAR_INT;
373 javaChar = "I";
374 break;
375 case eLongValue:
376 dcChar = DC_SIGCHAR_LONGLONG;
377 javaChar = "J";
378 break;
379 case eSizeTValue:
380 javaChar = "J";
381 if (Platform.SIZE_T_SIZE == 8) {
382 dcChar = DC_SIGCHAR_LONGLONG;
383 } else {
384 dcChar = DC_SIGCHAR_INT;
385 direct = false;
386 }
387 break;
388 case eShortValue:
389 dcChar = DC_SIGCHAR_SHORT;
390 javaChar = "S";
391 break;
392 case eDoubleValue:
393 dcChar = DC_SIGCHAR_DOUBLE;
394 javaChar = "D";
395 break;
396 case eFloatValue:
397 dcChar = DC_SIGCHAR_FLOAT;
398 javaChar = "F";
399 break;
400 case eByteValue:
401 dcChar = DC_SIGCHAR_CHAR;
402 javaChar = "B";
403 break;
404 case eBooleanValue:
405 dcChar = DC_SIGCHAR_BOOL;
406 javaChar = "Z";
407 break;
408 case eWCharValue:
409 switch (Platform.WCHAR_T_SIZE) {
410 case 1:
411 dcChar = DC_SIGCHAR_CHAR;
412 direct = false;
413 break;
414 case 2:
415 dcChar = DC_SIGCHAR_SHORT;
416 break;
417 case 4:
418 dcChar = DC_SIGCHAR_INT;
419 direct = false;
420 break;
421 default:
422 throw new RuntimeException("Unhandled sizeof(wchar_t) in GetJavaTypeSignature: " + Platform.WCHAR_T_SIZE);
423 }
424 javaChar = "C";
425 break;
426 case eIntFlagSet:
427 dcChar = DC_SIGCHAR_INT;
428 javaChar = "L" + parameterType.getName().replace('.', '/') + ";";//"Lorg/bridj/ValuedEnum;";
429 direct = false;
430 break;
431 case eCLongObjectValue:
432 dcChar = DC_SIGCHAR_POINTER;
433 javaChar = "Lorg/bridj/CLong;";
434 direct = false;
435 break;
436 case eSizeTObjectValue:
437 dcChar = DC_SIGCHAR_POINTER;
438 javaChar = "Lorg/bridj/SizeT;";
439 direct = false;
440 break;
441 case eTimeTObjectValue:
442 dcChar = DC_SIGCHAR_POINTER;
443 javaChar = "Lorg/bridj/TimeT;";
444 direct = false;
445 break;
446 case ePointerValue:
447 dcChar = DC_SIGCHAR_POINTER;
448 javaChar = "L" + parameterType.getName().replace('.', '/') + ";";
449 // javaChar = "Lorg/bridj/Pointer;";
450 direct = false;
451 break;
452 case eNativeObjectValue:
453 dcChar = DC_SIGCHAR_STRUCT; // TODO : unroll struct signature ?
454 javaChar = "L" + parameterType.getName().replace('.', '/') + ";";
455 direct = false;
456 // if (parameterType.equals(declaringClass)) {
457 // // special case of self-returning pointers
458 // dcChar = DC_SIGCHAR_POINTER;
459 break;
460 case eEllipsis:
461 javaChar = "[Ljava/lang/Object;";
462 dcChar = '?';
463 break;
464 default:
465 direct = false;
466 throw new RuntimeException("Unhandled " + ValueType.class.getSimpleName() + ": " + type);
467 }
468 if (genericParameterType instanceof ParameterizedType && iParam < 0)
469 {
470 ParameterizedType pt = (ParameterizedType)genericParameterType;
471 // TODO handle all cases !!!
472 Type[] ts = pt.getActualTypeArguments();
473 if (ts != null && ts.length == 1) {
474 Type t = ts[0];
475 if (t instanceof ParameterizedType)
476 t = ((ParameterizedType)t).getRawType();
477 if (t instanceof Class) {
478 Class c = (Class)t;
479 if (javaChar.endsWith(";")) {
480 asmChar = javaChar.substring(0, javaChar.length() - 1) + "<*L" + c.getName().replace('.', '/') + ";>";
481 //asmChar += ";";
482 }
483 }
484 }
485 }
486 if (javaSig != null)
487 javaSig.append(javaChar);
488 if (asmChar == null)
489 asmChar = javaChar;
490 if (asmSig != null)
491 asmSig.append(asmChar);
492 if (dcSig != null)
493 dcSig.append(dcChar);
494 }
495 /*
496 public void setDefinition(Method definition) {
497 this.definition = definition;
498 }
499
500 public Method getDefinition() {
501 return definition;
502 }
503 */
504
505
506 public void setMethod(Method method) {
507 this.method = method;
508 if (method != null)
509 this.methodName = method.getName();
510 if (declaringClass == null)
511 setDeclaringClass(method.getDeclaringClass());
512
513 }
514
515 public void setJavaSignature(String javaSignature) {
516 this.javaSignature = javaSignature;
517 }
518
519
520
521 public Method getMethod() {
522 return method;
523 }
524
525
526 public void setDeclaringClass(Class<?> declaringClass) {
527 this.declaringClass = declaringClass;
528 }
529
530
531 public Class<?> getDeclaringClass() {
532 return declaringClass;
533 }
534
535
536 public void setForwardedPointer(long forwardedPointer) {
537 this.forwardedPointer = forwardedPointer;
538 }
539
540
541 public long getForwardedPointer() {
542 return forwardedPointer;
543 }
544
545
546 /**
547 * Used for C++ virtual indexes and for struct fields ids
548 * @param virtualIndex
549 */
550 public void setVirtualIndex(int virtualIndex) {
551 //new RuntimeException("Setting virtualIndex of " + getMethod().getName() + " = " + virtualIndex).printStackTrace();
552 this.virtualIndex = virtualIndex;
553
554 if (BridJ.veryVerbose) {
555 BridJ.info("\t-> virtualIndex " + virtualIndex);
556 }
557 }
558
559
560 public int getVirtualIndex() {
561 return virtualIndex;
562 }
563
564 public String getSymbolName() {
565 return symbolName;
566 }
567 public void setSymbolName(String symbolName) {
568 this.symbolName = symbolName;
569 }
570
571 static char getDcCallbackConvention(int dcCallingConvention) {
572 switch (dcCallingConvention) {
573 case DC_CALL_C_X86_WIN32_STD :
574 return DC_SIGCHAR_CC_STDCALL;
575 case DC_CALL_C_X86_WIN32_FAST_MS :
576 return DC_SIGCHAR_CC_FASTCALL_MS;
577 case DC_CALL_C_X86_WIN32_FAST_GNU :
578 return DC_SIGCHAR_CC_FASTCALL_GNU;
579 case DC_CALL_C_X86_WIN32_THIS_MS :
580 return DC_SIGCHAR_CC_THISCALL_MS;
581 default:
582 return 0;
583 }
584 }
585
586 public void setDcCallingConvention(int dcCallingConvention) {
587 hasCC = true;
588 this.dcCallingConvention = dcCallingConvention;
589 }
590
591
592 public int getDcCallingConvention() {
593 return dcCallingConvention;
594 }
595
596 public Object getJavaCallback() {
597 return javaCallback;
598 }
599
600 public void setJavaCallback(Object javaCallback) {
601 this.javaCallback = javaCallback;
602 }
603
604 public void setGenericCallback(boolean genericCallback) {
605 this.isGenericCallback = genericCallback;
606 }
607
608 public boolean isGenericCallback() {
609 return isGenericCallback;
610 }
611
612 public void setNativeClass(long nativeClass) {
613 this.nativeClass = nativeClass;
614 }
615 }