/** * Copyright 2013, Landz and its contributors. All rights reserved. * * 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 z.znr.invoke.linux.x64; import com.kenai.jffi.*; import jdk.internal.org.objectweb.asm.ClassVisitor; import jdk.internal.org.objectweb.asm.ClassWriter; import z.znr.InlineAssembler; import z.znr.invoke.types.ParameterType; import z.znr.invoke.types.ResultType; import z.znr.invoke.types.Signature; import java.io.OutputStreamWriter; import java.lang.invoke.MethodHandle; import java.util.Collection; import java.util.Collections; import java.util.EnumMap; import java.util.Map; import static jdk.internal.org.objectweb.asm.Opcodes.*; import static z.znr.invoke.linux.x64.AsmUtil.*; import static z.znr.invoke.linux.x64.CodegenUtils.*; /** * */ final class DefaultMethodHandleGenerator implements MethodHandleGenerator { @Override public MethodHandle createBoundHandle(Signature signature, InlineAssembler inlineAssembler) { AsmClassLoader classLoader = new AsmClassLoader(Native.class.getClassLoader()); ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); ClassVisitor cv = Native.DEBUG ? AsmUtil.newCheckClassAdapter(cw) : cw; AsmBuilder builder = new AsmBuilder(p(Native.class) + "$jnr$ffi$" + Native.nextClassID.getAndIncrement(), cv, classLoader); cv.visit(V1_7, ACC_PUBLIC | ACC_FINAL, builder.getClassNamePath(), null, p(Object.class), new String[0]); ResultType resultType = signature.getResultType().asPrimitiveType(); generate(builder, Native.STUB_NAME, signature.getNativeCallContext(), inlineAssembler, resultType, signature.parameterTypeArray()); emitDefaultConstructor(cv); emitStaticFieldInitialization(builder, cv); cv.visitEnd(); try { Class implClass = classLoader.defineClass(builder.getClassNamePath().replace("/", "."), cw.toByteArray(), Native.DEBUG ? new OutputStreamWriter(System.err) : null); return Native.LOOKUP.findStatic(implClass, Native.STUB_NAME, signature.methodType()); } catch (Throwable ex) { throw new RuntimeException(ex); } } public boolean isSupported(ResultType resultType, Collection<ParameterType> parameterTypes) { for (ParameterType parameterType : parameterTypes) { if (!parameterType.javaType().isPrimitive() && !parameterType.isObject()) { return false; } } return resultType.javaType().isPrimitive(); } private static void generate(AsmBuilder builder, String functionName, CallContext callContext, InlineAssembler inlineAssembler, ResultType resultType, ParameterType[] parameterTypes) { SkinnyMethodAdapter mv = new SkinnyMethodAdapter(builder.getClassVisitor(), ACC_PUBLIC | ACC_FINAL | ACC_STATIC, functionName, sig(resultType.javaType(), Util.javaTypeArray(parameterTypes)), null, null); mv.start(); // Retrieve the jffi Invoker instance mv.getstatic(builder.getClassNamePath(), builder.getObjectFieldName(Invoker.getInstance(), com.kenai.jffi.Invoker.class), ci(com.kenai.jffi.Invoker.class)); // retrieve the call context and function address mv.getstatic(builder.getClassNamePath(), builder.getObjectFieldName(callContext), ci(CallContext.class)); long nativeAddress = Util.inlineAssemblerToCodeAddress(inlineAssembler).address(); mv.ldc(nativeAddress); // Stash a strong ref to the library, so it doesn't get garbage collected. builder.getObjectField(nativeAddress); LocalVariableAllocator localVariableAllocator = new LocalVariableAllocator(parameterTypes); // [ stack contains: Invoker, Function ] // Create a new InvocationBuffer mv.getstatic(builder.getClassNamePath(), builder.getObjectFieldName(callContext), ci(CallContext.class)); mv.invokestatic(AsmRuntime.class, "newHeapInvocationBuffer", HeapInvocationBuffer.class, CallContext.class); // [ stack contains: Invoker, Function, HeapInvocationBuffer ] final LocalVariable[] parameters = AsmUtil.getParameterVariables(parameterTypes, true); for (int i = 0; i < parameterTypes.length; ++i) { MarshalOp marshalOp = getMarshalOp(parameterTypes[i].nativeType()); mv.dup(); // HeapInvocationBuffer load(mv, parameterTypes[i].javaType(), parameters[i]); if (parameterTypes[i].getObjectStrategyHandle() != null) { mv.getstatic(builder.getClassNamePath(), builder.getObjectFieldName(parameterTypes[i].getObjectStrategyHandle()), ci(MethodHandle.class)); load(mv, parameterTypes[i].javaType(), parameters[i]); mv.invokevirtual(MethodHandle.class, "invokeExact", ObjectParameterStrategy.class, parameterTypes[i].javaType()); mv.getstatic(builder.getClassNamePath(), builder.getObjectFieldName(ObjectParameterInfo.create(i, parameterTypes[i].getDataDirection().getArrayFlags())), ci(ObjectParameterInfo.class)); mv.invokevirtual(HeapInvocationBuffer.class, "putObject", void.class, Object.class, com.kenai.jffi.ObjectParameterStrategy.class, ObjectParameterInfo.class); } else { NumberUtil.convertPrimitive(mv, parameterTypes[i].javaType(), marshalOp.getPrimitiveClass(), parameterTypes[i].nativeType()); mv.invokevirtual(HeapInvocationBuffer.class, marshalOp.getMethodName(), void.class, marshalOp.getPrimitiveClass()); } } InvokeOp iop = getInvokeOp(resultType); mv.invokevirtual(Invoker.class, iop.getMethodName(), iop.getPrimitiveClass(), CallContext.class, long.class, HeapInvocationBuffer.class); // narrow/widen the return value if needed NumberUtil.convertPrimitive(mv, iop.getPrimitiveClass(), resultType.javaType(), resultType.nativeType()); emitReturnOp(mv, resultType.javaType()); mv.visitMaxs(100, localVariableAllocator.getSpaceUsed()); mv.visitEnd(); } private static InvokeOp getInvokeOp(ResultType resultType) { InvokeOp iop = invokeOps.get(resultType.nativeType()); if (iop == null) { throw new IllegalArgumentException("unsupported return type " + resultType.javaType()); } return iop; } private static MarshalOp getMarshalOp(z.znr.invoke.types.NativeType nativeType) { MarshalOp marshalOp = marshalOps.get(nativeType); if (marshalOp == null) { throw new IllegalArgumentException("unsupported parameter type " + nativeType); } return marshalOp; } private static abstract class Operation { private final String methodName; private final Class primitiveClass; private Operation(String methodName, Class primitiveClass) { this.methodName = methodName; this.primitiveClass = primitiveClass; } public String getMethodName() { return methodName; } public Class getPrimitiveClass() { return primitiveClass; } } private static final class MarshalOp extends Operation { private MarshalOp(String methodName, Class primitiveClass) { super("put" + methodName, primitiveClass); } } private static final class InvokeOp extends Operation { private InvokeOp(String methodName, Class primitiveClass) { super("invoke" + methodName, primitiveClass); } } private static final Map<z.znr.invoke.types.NativeType, MarshalOp> marshalOps; private static final Map<z.znr.invoke.types.NativeType, InvokeOp> invokeOps; static { Map<z.znr.invoke.types.NativeType, MarshalOp> mops = new EnumMap<z.znr.invoke.types.NativeType, MarshalOp>(z.znr.invoke.types.NativeType.class); Map<z.znr.invoke.types.NativeType, InvokeOp> iops = new EnumMap<z.znr.invoke.types.NativeType, InvokeOp>(z.znr.invoke.types.NativeType.class); mops.put(z.znr.invoke.types.NativeType.SCHAR, new MarshalOp("Byte", int.class)); mops.put(z.znr.invoke.types.NativeType.UCHAR, new MarshalOp("Byte", int.class)); mops.put(z.znr.invoke.types.NativeType.SSHORT, new MarshalOp("Short", int.class)); mops.put(z.znr.invoke.types.NativeType.USHORT, new MarshalOp("Short", int.class)); mops.put(z.znr.invoke.types.NativeType.SINT, new MarshalOp("Int", int.class)); mops.put(z.znr.invoke.types.NativeType.UINT, new MarshalOp("Int", int.class)); mops.put(z.znr.invoke.types.NativeType.SLONG_LONG, new MarshalOp("Long", long.class)); mops.put(z.znr.invoke.types.NativeType.ULONG_LONG, new MarshalOp("Long", long.class)); mops.put(z.znr.invoke.types.NativeType.FLOAT, new MarshalOp("Float", float.class)); mops.put(z.znr.invoke.types.NativeType.DOUBLE, new MarshalOp("Double", double.class)); mops.put(z.znr.invoke.types.NativeType.POINTER, new MarshalOp("Address", long.class)); if (Util.sizeof(z.znr.invoke.types.NativeType.SLONG) == 4) { mops.put(z.znr.invoke.types.NativeType.SLONG, new MarshalOp("Int", int.class)); mops.put(z.znr.invoke.types.NativeType.ULONG, new MarshalOp("Int", int.class)); } else { mops.put(z.znr.invoke.types.NativeType.SLONG, new MarshalOp("Long", long.class)); mops.put(z.znr.invoke.types.NativeType.ULONG, new MarshalOp("Long", long.class)); } iops.put(z.znr.invoke.types.NativeType.SCHAR, new InvokeOp("Int", int.class)); iops.put(z.znr.invoke.types.NativeType.UCHAR, new InvokeOp("Int", int.class)); iops.put(z.znr.invoke.types.NativeType.SSHORT, new InvokeOp("Int", int.class)); iops.put(z.znr.invoke.types.NativeType.USHORT, new InvokeOp("Int", int.class)); iops.put(z.znr.invoke.types.NativeType.SINT, new InvokeOp("Int", int.class)); iops.put(z.znr.invoke.types.NativeType.UINT, new InvokeOp("Int", int.class)); iops.put(z.znr.invoke.types.NativeType.VOID, new InvokeOp("Int", int.class)); iops.put(z.znr.invoke.types.NativeType.SLONG_LONG, new InvokeOp("Long", long.class)); iops.put(z.znr.invoke.types.NativeType.ULONG_LONG, new InvokeOp("Long", long.class)); iops.put(z.znr.invoke.types.NativeType.FLOAT, new InvokeOp("Float", float.class)); iops.put(z.znr.invoke.types.NativeType.DOUBLE, new InvokeOp("Double", double.class)); iops.put(z.znr.invoke.types.NativeType.POINTER, new InvokeOp("Address", long.class)); if (Util.sizeof(z.znr.invoke.types.NativeType.SLONG) == 4) { iops.put(z.znr.invoke.types.NativeType.SLONG, new InvokeOp("Int", int.class)); iops.put(z.znr.invoke.types.NativeType.ULONG, new InvokeOp("Int", int.class)); } else { iops.put(z.znr.invoke.types.NativeType.SLONG, new InvokeOp("Long", long.class)); iops.put(z.znr.invoke.types.NativeType.ULONG, new InvokeOp("Long", long.class)); } marshalOps = Collections.unmodifiableMap(mops); invokeOps = Collections.unmodifiableMap(iops); } }