jnr.ffi.provider.jffi.AsmLibraryLoader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jnr-ffi Show documentation
Show all versions of jnr-ffi Show documentation
A library for invoking native functions from java
/*
* Copyright (C) 2008-2010 Wayne Meissner
*
* This file is part of the JNR project.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jnr.ffi.provider.jffi;
import com.kenai.jffi.CallingConvention;
import jnr.ffi.*;
import jnr.ffi.annotations.StdCall;
import jnr.ffi.mapper.*;
import jnr.ffi.provider.IdentityFunctionMapper;
import jnr.ffi.provider.NullTypeMapper;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import static jnr.ffi.provider.jffi.CodegenUtils.*;
import static jnr.ffi.provider.jffi.InvokerUtil.*;
import static jnr.ffi.util.Annotations.sortedAnnotationCollection;
import static org.objectweb.asm.Opcodes.*;
public class AsmLibraryLoader extends LibraryLoader {
public final static boolean DEBUG = Boolean.getBoolean("jnr.ffi.compile.dump");
private static final AtomicLong nextClassID = new AtomicLong(0);
private static final AtomicLong uniqueId = new AtomicLong(0);
private static final ThreadLocal classLoader = new ThreadLocal();
private final NativeRuntime runtime = NativeRuntime.getInstance();
@Override
T loadLibrary(NativeLibrary library, Class interfaceClass, Map libraryOptions) {
AsmClassLoader oldClassLoader = classLoader.get();
// Only create a new class loader if this was not a recursive call (i.e. loading a library as a result of loading another library)
if (oldClassLoader == null) {
classLoader.set(new AsmClassLoader(interfaceClass.getClassLoader()));
}
try {
return generateInterfaceImpl(library, interfaceClass, libraryOptions, classLoader.get());
} finally {
if (oldClassLoader == null) classLoader.remove();
}
}
private T generateInterfaceImpl(final NativeLibrary library, Class interfaceClass, Map 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 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, "",
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), "", 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 implClass = classLoader.defineClass(builder.getClassNamePath().replace("/", "."), bytes);
Constructor cons = implClass.getDeclaredConstructor(jnr.ffi.Runtime.class, NativeLibrary.class, Object[].class);
T result = cons.newInstance(runtime, library, builder.getObjectFieldValues());
// Attach any native method stubs - we have to delay this until the
// implementation class is loaded for it to work.
System.err.flush();
System.out.flush();
compiler.attach(implClass);
return result;
} catch (Throwable ex) {
throw new RuntimeException(ex);
}
}
private static com.kenai.jffi.CallingConvention getCallingConvention(Class interfaceClass, Map options) {
if (interfaceClass.isAnnotationPresent(StdCall.class)) {
return com.kenai.jffi.CallingConvention.STDCALL;
}
return InvokerUtil.getCallingConvention(options);
}
private void generateFunctionNotFound(ClassVisitor cv, String className, String errorFieldName, String functionName,
Class returnType, Class[] parameterTypes) {
SkinnyMethodAdapter mv = new SkinnyMethodAdapter(cv, ACC_PUBLIC | ACC_FINAL, functionName,
sig(returnType, parameterTypes), null, null);
mv.start();
mv.getstatic(className, errorFieldName, ci(String.class));
mv.invokestatic(AsmRuntime.class, "newUnsatisifiedLinkError", UnsatisfiedLinkError.class, String.class);
mv.athrow();
mv.visitMaxs(10, 10);
mv.visitEnd();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy