private <T> T generateInterfaceImpl(final NativeLibrary library, Class<T> interfaceClass, Map<LibraryOption, ?> libraryOptions,
AsmClassLoader classLoader) {
boolean debug = DEBUG && !interfaceClass.isAnnotationPresent(NoTrace.class);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
ClassVisitor cv = debug ? AsmUtil.newCheckClassAdapter(cw) : cw;
AsmBuilder builder = new AsmBuilder(runtime, p(interfaceClass) + "$jnr$ffi$" + nextClassID.getAndIncrement(), cv, classLoader);
cv.visit(V1_6, ACC_PUBLIC | ACC_FINAL, builder.getClassNamePath(), null, p(AbstractAsmLibraryInterface.class),
new String[] { p(interfaceClass) });
FunctionMapper functionMapper = libraryOptions.containsKey(LibraryOption.FunctionMapper)
? (FunctionMapper) libraryOptions.get(LibraryOption.FunctionMapper) : IdentityFunctionMapper.getInstance();
SignatureTypeMapper typeMapper;
if (libraryOptions.containsKey(LibraryOption.TypeMapper)) {
Object tm = libraryOptions.get(LibraryOption.TypeMapper);
if (tm instanceof SignatureTypeMapper) {
typeMapper = (SignatureTypeMapper) tm;
} else if (tm instanceof TypeMapper) {
typeMapper = new SignatureTypeMapperAdapter((TypeMapper) tm);
} else {
throw new IllegalArgumentException("TypeMapper option is not a valid TypeMapper instance");
}
} else {
typeMapper = new NullTypeMapper();
}
typeMapper = new CompositeTypeMapper(typeMapper, new CachingTypeMapper(new InvokerTypeMapper(new NativeClosureManager(runtime, typeMapper, classLoader), classLoader)));
com.kenai.jffi.CallingConvention libraryCallingConvention = getCallingConvention(interfaceClass, libraryOptions);
StubCompiler compiler = StubCompiler.newCompiler(runtime);
final MethodGenerator[] generators = {
!interfaceClass.isAnnotationPresent(NoX86.class)
? new X86MethodGenerator(compiler) : new NotImplMethodGenerator(),
new FastIntMethodGenerator(),
new FastLongMethodGenerator(),
new FastNumericMethodGenerator(),
new BufferMethodGenerator()
};
for (Method m : interfaceClass.getMethods()) {
if (Variable.class.isAssignableFrom(m.getReturnType())) {
continue;
}
Collection<Annotation> annotations = sortedAnnotationCollection(m.getAnnotations());
String functionName = functionMapper.mapFunctionName(m.getName(), new NativeFunctionMapperContext(library, annotations));
// Allow individual methods to set the calling convention to stdcall
CallingConvention callingConvention = m.isAnnotationPresent(StdCall.class)
? CallingConvention.STDCALL : libraryCallingConvention;
boolean saveErrno = InvokerUtil.requiresErrno(m);
try {
generateFunctionInvocation(runtime, builder, m, library.findSymbolAddress(functionName),
callingConvention, saveErrno, typeMapper, generators);
} catch (SymbolNotFoundError ex) {
String errorFieldName = "error_" + uniqueId.incrementAndGet();
cv.visitField(ACC_PRIVATE | ACC_FINAL | ACC_STATIC, errorFieldName, ci(String.class), null, ex.getMessage());
generateFunctionNotFound(cv, builder.getClassNamePath(), errorFieldName, functionName, m.getReturnType(), m.getParameterTypes());
}
}
// generate global variable accessors
VariableAccessorGenerator variableAccessorGenerator = new VariableAccessorGenerator(runtime);
for (Method m : interfaceClass.getMethods()) {
if (Variable.class == m.getReturnType()) {
java.lang.reflect.Type variableType = ((ParameterizedType) m.getGenericReturnType()).getActualTypeArguments()[0];
if (!(variableType instanceof Class)) {
throw new IllegalArgumentException("unsupported variable class: " + variableType);
}
String functionName = functionMapper.mapFunctionName(m.getName(), null);
try {
variableAccessorGenerator.generate(builder, interfaceClass, m.getName(),
library.findSymbolAddress(functionName), (Class) variableType, sortedAnnotationCollection(m.getAnnotations()),
typeMapper, classLoader);
} catch (SymbolNotFoundError ex) {
String errorFieldName = "error_" + uniqueId.incrementAndGet();
cv.visitField(ACC_PRIVATE | ACC_FINAL | ACC_STATIC, errorFieldName, ci(String.class), null, ex.getMessage());
generateFunctionNotFound(cv, builder.getClassNamePath(), errorFieldName, functionName, m.getReturnType(), m.getParameterTypes());
}
}
}
// Create the constructor to set the instance fields
SkinnyMethodAdapter init = new SkinnyMethodAdapter(cv, ACC_PUBLIC, "<init>",
sig(void.class, jnr.ffi.Runtime.class, NativeLibrary.class, Object[].class),
null, null);
init.start();
// Invoke the super class constructor as super(Library)
init.aload(0);
init.aload(1);
init.aload(2);
init.invokespecial(p(AbstractAsmLibraryInterface.class), "<init>", sig(void.class, jnr.ffi.Runtime.class, NativeLibrary.class));
builder.emitFieldInitialization(init, 3);
init.voidreturn();
init.visitMaxs(10, 10);
init.visitEnd();
cv.visitEnd();
try {
byte[] bytes = cw.toByteArray();
if (debug) {
ClassVisitor trace = AsmUtil.newTraceClassVisitor(new PrintWriter(System.err));
new ClassReader(bytes).accept(trace, 0);
}
Class<T> implClass = classLoader.defineClass(builder.getClassNamePath().replace("/", "."), bytes);
Constructor<T> cons = implClass.getDeclaredConstructor(jnr.ffi.Runtime.class, NativeLibrary.class, Object[].class);