/** * 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.Platform; import z.znr.InlineAssembler; import z.znr.invoke.types.*; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.util.Arrays; import java.util.Collection; final class PrimitiveNumericMethodHandleGenerator implements MethodHandleGenerator { private static final boolean ENABLED = Util.getBooleanProperty("jnr.invoke.fast-numeric.enabled", true); private static final int MAX_PARAMETERS = getMaximumParameters('N', long.class); PrimitiveNumericMethodHandleGenerator() { } public MethodHandle createBoundHandle(Signature signature, InlineAssembler inlineAssembler) { try { MethodHandle mh = MethodHandles.filterArguments(createUnBoundHandle(signature), 0, Native.LOOKUP.findVirtual(CodeAddress.class, "address", MethodType.methodType(long.class))); mh = MethodHandles.insertArguments(mh, 0, Util.inlineAssemblerToCodeAddress(inlineAssembler).address()); return MethodHandles.explicitCastArguments(mh, signature.methodType()); } catch (NoSuchMethodException | IllegalAccessException e) { throw new RuntimeException(e); } } public MethodHandle createUnBoundHandle(Signature signature) { MethodHandle mh = MethodHandles.insertArguments(getFastNumericHandle(signature), 0, signature.getNativeCallContext()); Class nativeIntType = long.class; for (int i = 0; i < signature.getParameterCount(); i++) { ParameterType parameterType = signature.getParameterType(i); MethodHandle conversion = NumberUtil.getParameterConversionHandle(parameterType.nativeType(), parameterType.javaType(), nativeIntType); if (conversion != null) { mh = MethodHandles.filterArguments(mh, i + 1, conversion); } } MethodHandle conversion = NumberUtil.getResultConversionHandle(signature.getResultType().nativeType(), nativeIntType, signature.getResultType().javaType()); if (conversion != null) { mh = MethodHandles.filterReturnValue(mh, conversion); } return mh; } public boolean isSupported(ResultType resultType, Collection<ParameterType> parameterTypes) { if (!ENABLED) { return false; } final Platform platform = Platform.getPlatform(); // Only supported on i386 and amd64 arches if (platform.getCPU() != Platform.CPU.X86_64) { return false; } if (platform.getOS().equals(Platform.OS.WINDOWS)) { return false; } for (ParameterType parameterType : parameterTypes) { if (!isFastNumericParameter(parameterType)) { return false; } } return isFastNumericResult(resultType); } private static MethodHandle getFastNumericHandle(Signature signature) { return getPrimitiveInvokerHandle(signature, long.class, 'N'); } private static MethodHandle getPrimitiveInvokerHandle(Signature signature, Class nativeIntType, char suffix) { MethodType mt = MethodType.methodType(long.class, getInvokerParameterClasses(signature.getParameterCount(), nativeIntType)); try { return Native.LOOKUP.findVirtual(com.kenai.jffi.Invoker.class, "invoke" + suffix + signature.getParameterCount(), mt) .bindTo(com.kenai.jffi.Invoker.getInstance()); } catch (NoSuchMethodException | IllegalAccessException e) { throw new RuntimeException(e); } } private static boolean isNumericType(SignatureType type) { switch (type.nativeType()) { case SCHAR: case UCHAR: case SSHORT: case USHORT: case SINT: case UINT: case SLONG: case ULONG: case SLONG_LONG: case ULONG_LONG: case FLOAT: case DOUBLE: case POINTER: return type.javaType().isPrimitive(); default: return false; } } static boolean isFastNumericResult(ResultType type) { return isNumericType(type) || (type.nativeType() == NativeType.VOID && void.class == type.javaType()) ; } static boolean isFastNumericParameter(ParameterType parameterType) { return isNumericType(parameterType); } private static Class[] getInvokerParameterClasses(int parameterCount, Class nativeIntType) { Class[] ptypes = new Class[parameterCount + 2]; ptypes[0] = com.kenai.jffi.CallContext.class; ptypes[1] = long.class; Arrays.fill(ptypes, 2, ptypes.length, nativeIntType); return ptypes; } static int getMaximumParameters(char suffix, Class nativeIntType) { try { com.kenai.jffi.Invoker.class.getDeclaredMethod("invoke" + suffix + "6", getInvokerParameterClasses(6, nativeIntType)); return 6; } catch (Throwable t) { return -1; } } }