Skip to content

Commit

Permalink
重构 FFM API (#55)
Browse files Browse the repository at this point in the history
  • Loading branch information
scx567888 authored Sep 23, 2024
1 parent bbc53c9 commit aeabe5f
Show file tree
Hide file tree
Showing 60 changed files with 1,118 additions and 382 deletions.
6 changes: 3 additions & 3 deletions scx-common/src/main/java/cool/scx/common/ansi/AnsiHelper.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package cool.scx.common.ansi;

import cool.scx.common.ffm.type.IntRef;
import cool.scx.common.ffm.type.mapper.IntMapper;
import cool.scx.common.util.OSHelper;

import static cool.scx.common.ffm.win32.Kernel32.KERNEL32;
import static cool.scx.common.ffm.platform.win32.Kernel32.KERNEL32;
import static cool.scx.common.standard.OSType.WINDOWS;

class AnsiHelper {
Expand All @@ -23,7 +23,7 @@ static void enableWindows10AnsiSupport() {
var hOut = KERNEL32.GetStdHandle(-11);

// See https://learn.microsoft.com/zh-cn/windows/console/getconsolemode
var lpModeMemorySegment = new IntRef(0);
var lpModeMemorySegment = new IntMapper(0);
KERNEL32.GetConsoleMode(hOut, lpModeMemorySegment);

// See https://learn.microsoft.com/zh-cn/windows/console/setconsolemode
Expand Down
241 changes: 131 additions & 110 deletions scx-common/src/main/java/cool/scx/common/ffm/FFMHelper.java
Original file line number Diff line number Diff line change
@@ -1,146 +1,167 @@
package cool.scx.common.ffm;

import cool.scx.common.ffm.type.*;
import cool.scx.common.reflect.ReflectFactory;
import cool.scx.common.ffm.type.callback.Callback;
import cool.scx.common.ffm.type.mapper.*;
import cool.scx.common.ffm.type.paramter.*;
import cool.scx.common.ffm.type.struct.Struct;
import cool.scx.common.ffm.type.wrapper.*;

import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;

import static java.lang.foreign.Linker.nativeLinker;
import static java.lang.foreign.ValueLayout.*;
import static java.lang.invoke.MethodHandles.lookup;

public final class FFMHelper {

/**
* 从指定的 SymbolLookup 中寻找 MethodHandle
*
* @param symbolLookup symbolLookup
* @param name 方法名称
* @param functionDescriptor 方法描述
* @return MethodHandle
*/
public static MethodHandle findMethodHandle(SymbolLookup symbolLookup, String name, FunctionDescriptor functionDescriptor) {
var memorySegment = symbolLookup.find(name).orElseThrow(() -> new IllegalArgumentException("未找到 " + name));
return nativeLinker().downcallHandle(memorySegment, functionDescriptor);
}

/**
* 根据 Method 获取 对应的方法描述
*
* @param method method
* @return 对应的方法描述
*/
public static FunctionDescriptor getFunctionDescriptor(Method method) {
return FunctionDescriptor.of(getMemoryLayout(method.getReturnType()), getMemoryLayouts(method.getParameterTypes()));
}

/**
* 根据 Class 获取对应的 MemoryLayout 类型
*
* @param type 类型
* @return 对应的 MemoryLayout 类型
*/
public static MemoryLayout getMemoryLayout(Class<?> type) {
//1, 先处理可以直接映射的基本类型
if (type == Byte.class || type == byte.class) {
return JAVA_BYTE;
}
if (type == Boolean.class || type == boolean.class) {
return JAVA_BOOLEAN;
}
if (type == Character.class || type == char.class) {
return JAVA_CHAR;
}
if (type == Short.class || type == short.class) {
return JAVA_SHORT;
}
if (type == Integer.class || type == int.class) {
return JAVA_INT;
}
if (type == Long.class || type == long.class) {
return JAVA_LONG;
} else if (type == Integer.class || type == int.class) {
return JAVA_INT;
} else if (type == Boolean.class || type == boolean.class) {
}
if (type == Float.class || type == float.class) {
return JAVA_FLOAT;
}
if (type == Double.class || type == double.class) {
return JAVA_DOUBLE;
}
if (type == MemorySegment.class) {
return ADDRESS;
}
//2, 处理基本类型的简单包装类型
if (ByteWrapper.class.isAssignableFrom(type)) {
return JAVA_BYTE;
}
if (BooleanWrapper.class.isAssignableFrom(type)) {
return JAVA_BOOLEAN;
} else if (type == String.class ||
type == MemorySegment.class ||
type.isArray() ||
Ref.class.isAssignableFrom(type) ||
Callback.class.isAssignableFrom(type) ||
Struct.class.isAssignableFrom(type)) {
}
if (CharWrapper.class.isAssignableFrom(type)) {
return JAVA_CHAR;
}
if (ShortWrapper.class.isAssignableFrom(type)) {
return JAVA_SHORT;
}
if (IntWrapper.class.isAssignableFrom(type)) {
return JAVA_INT;
}
if (LongWrapper.class.isAssignableFrom(type)) {
return JAVA_LONG;
}
if (FloatWrapper.class.isAssignableFrom(type)) {
return JAVA_FLOAT;
}
if (DoubleWrapper.class.isAssignableFrom(type)) {
return JAVA_DOUBLE;
}
if (AddressWrapper.class.isAssignableFrom(type)) {
return ADDRESS;
}
//3, 处理字符串
if (String.class == type) {
return ADDRESS;
}
//4, 处理映射类型
if (Mapper.class.isAssignableFrom(type)) {
return ADDRESS;
}
//5, 处理 结构体类型 这里我们使用 ADDRESS 而不使用 MemoryLayout.structLayout(), 因为需要在运行时才知道具体结构
if (Struct.class.isAssignableFrom(type)) {
return ADDRESS;
}
//6, 处理 Callback 类型
if (Callback.class.isAssignableFrom(type)) {
return ADDRESS;
}
//7, 处理基本类型的数组类型 这里我们使用 ADDRESS 而不使用 MemoryLayout.sequenceLayout() , 因为我们不知道数组长度
if (type.isArray() && type.getComponentType().isPrimitive()) {
return ADDRESS;
}
//8, Parameter 类型 这里一定要放在最后 因为其子类 Wrapper 需要单独处理
if (Parameter.class.isAssignableFrom(type)) {
return ADDRESS;
}
throw new IllegalArgumentException("不支持的参数类型 !!! " + type);
}

/**
* 根据 Class 获取对应的 MemoryLayout 类型
*
* @param types 类型
* @return 对应的 MemoryLayout 类型
*/
public static MemoryLayout[] getMemoryLayouts(Class<?>[] types) {
var array = new MemoryLayout[types.length];
var memoryLayouts = new MemoryLayout[types.length];
for (var i = 0; i < types.length; i = i + 1) {
array[i] = getMemoryLayout(types[i]);
memoryLayouts[i] = getMemoryLayout(types[i]);
}
return array;
return memoryLayouts;
}

//todo 需要内部改变的数据 如 数组 等等如何处理
public static Object convertParameter(Arena arena, Object o) throws NoSuchMethodException, IllegalAccessException {
public static Parameter convertToParameter(Object o) throws NoSuchMethodException, IllegalAccessException {
return switch (o) {
case null -> MemorySegment.NULL;
case Long _, MemorySegment _, Ref _, Integer _ -> o;
case String s -> arena.allocateFrom(s);
case char[] c -> new CharArrayRef(c);
case Callback c -> convertCallback(arena, c);
case Struct c -> new StructRef(c);
//0, 空值
case null -> new RawValueParameter(MemorySegment.NULL);
//1, 基本值
case Byte _,
Boolean _,
Character _,
Short _,
Integer _,
Long _,
Float _,
Double _,
MemorySegment _ -> new RawValueParameter(o);
//2, 基本值包装值
case Wrapper<?> w -> w;
//3, 字符串
case String s -> new StringParameter(s);
//4, 映射类型
case Mapper m -> new MapperParameter(m);
//5, 结构体
case Struct c -> new StructParameter(c);
//6, Callback 类型
case Callback c -> new CallbackParameter(c);
//7, 数组类型
case byte[] c -> new ArrayParameter(c, new ByteArrayMapper(c));
case char[] c -> new ArrayParameter(c, new CharArrayMapper(c));
case short[] c -> new ArrayParameter(c, new ShortArrayMapper(c));
case int[] c -> new ArrayParameter(c, new IntArrayMapper(c));
case long[] c -> new ArrayParameter(c, new LongArrayMapper(c));
case float[] c -> new ArrayParameter(c, new FloatArrayMapper(c));
case double[] c -> new ArrayParameter(c, new DoubleArrayMapper(c));
//8, Parameter 类型
case Parameter r -> r;
default -> throw new RuntimeException("无法转换的类型 !!! " + o.getClass());
};
}

public static MemorySegment convertCallback(Arena arena, Callback c) throws NoSuchMethodException, IllegalAccessException {
//todo 此处待处理
var cc = c.getClass().getInterfaces()[0];
var s = ReflectFactory.getClassInfo(cc);
var methodInfo = Arrays.stream(s.methods()).filter(method -> c.callbackMethodName().equals(method.name())).findFirst().orElseThrow(() -> new NoSuchMethodException("callback"));
var _handle = lookup().unreflect(methodInfo.method()).bindTo(c);
var r = getMemoryLayout(methodInfo.method().getReturnType());
var p = getMemoryLayouts(methodInfo.method().getParameterTypes());
return nativeLinker().upcallStub(_handle, FunctionDescriptor.of(r, p), arena);
}

public static Object[] convertParameters(Arena arena, Object[] o) throws NoSuchMethodException, IllegalAccessException {
var array = new Object[o.length];
for (var i = 0; i < o.length; i = i + 1) {
array[i] = convertParameter(arena, o[i]);
public static Parameter[] convertToParameters(Object[] objs) throws NoSuchMethodException, IllegalAccessException {
var result = new Parameter[objs.length];
for (var i = 0; i < objs.length; i = i + 1) {
result[i] = convertToParameter(objs[i]);
}
return array;
return result;
}

public static String toString(char[] buf) {
int len = buf.length;

public static Object[] initRefParameters(Arena arena, Object[] parameters) {
var array = new Object[parameters.length];
for (var i = 0; i < parameters.length; i = i + 1) {
array[i] = initRef(arena, parameters[i]);
}
return array;
}

/**
* 如果类型属于引用 则调用 init 方法
*
* @param arena a
* @param parameter a
* @return a
*/
private static Object initRef(Arena arena, Object parameter) {
if (parameter instanceof Ref r) {
return r.writeToMemorySegment(arena);
}
return parameter;
}

/**
* 如果类型属于引用 则调用 end 方法
*
* @param parameters p
*/
public static void refreshRefs(Object[] parameters) {
for (var parameter : parameters) {
if (parameter instanceof Ref r) {
r.readFromMemorySegment();
for (int index = 0; index < len; index = index + 1) {
if (buf[index] == 0) {
len = index;
break;
}
}

return len == 0 ? "" : new String(buf, 0, len);
}

}
Loading

0 comments on commit aeabe5f

Please sign in to comment.