Skip to content

Commit

Permalink
Add unicity of enum instances for a given value
Browse files Browse the repository at this point in the history
  • Loading branch information
HGuillemet committed Jun 3, 2021
1 parent 8c77e8a commit 59c1dfe
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 18 deletions.
86 changes: 86 additions & 0 deletions src/main/java/org/bytedeco/javacpp/EnumValueMap.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package org.bytedeco.javacpp;

import org.bytedeco.javacpp.tools.Logger;
import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.util.EnumSet;
import java.util.concurrent.ConcurrentHashMap;

public class EnumValueMap<E extends Enum<E>> extends ConcurrentHashMap<Long, E> {

private static final Logger logger = Logger.create(Loader.class);
private static final ConcurrentHashMap<Class<? extends Enum<?>>, EnumValueMap<?>> maps = new ConcurrentHashMap<>();

static public <E extends Enum<E>> E getEnum(long v, Class<E> c) {
EnumValueMap<E> evm = (EnumValueMap<E>) maps.get(c);
if (evm == null) {
evm = new EnumValueMap<>();
try {
Field valueField = c.getDeclaredField("value");
if (valueField.getType() == boolean.class)
for (E e : EnumSet.allOf(c))
evm.put(valueField.getBoolean(e) ? 1L : 0, e);
else
for (E e : EnumSet.allOf(c))
evm.put(valueField.getLong(e), e);
maps.put(c, evm);
} catch (NoSuchFieldException | IllegalAccessException e) {
logger.warn("Enum class " + c + " doesn't have an accessible value field");
}
}
E e = evm.get(v);
if (e == null)
e = evm.instantiate(v, c);
return e;
}

private static Unsafe UNSAFE;
static {
try {
Class<?> c = Class.forName("sun.misc.Unsafe");
Field f = c.getDeclaredField("theUnsafe");
f.setAccessible(true);
UNSAFE = (Unsafe) f.get(null);
} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
logger.warn("Unsafe not available");
}
}

private synchronized E instantiate(long l, Class<E> c) {
if (UNSAFE == null) return null;
try {
E e = (E) UNSAFE.allocateInstance(c);
Field ordinalField = Enum.class.getDeclaredField("ordinal");
ordinalField.setAccessible(true);
ordinalField.setInt(e, size());

Field nameField = Enum.class.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(e, "DYN_"+l);

Field valueField = c.getDeclaredField("value");
valueField.setAccessible(true);
Class<?> type = valueField.getType();
if (type == boolean.class)
valueField.setBoolean(e, l != 0);
else if (type == byte.class)
valueField.setByte(e, (byte) l);
else if (type == short.class)
valueField.setShort(e, (short) l);
else if (type == int.class)
valueField.setInt(e, (int) l);
else if (type == long.class)
valueField.setLong(e, l);
else {
logger.warn("Value type of " + c +" is not a boolean or an integral type");
return null;
}
put(l, e);
return e;
} catch (InstantiationException | NoSuchFieldException | IllegalAccessException exc) {
logger.warn("Cannot dynamically instantiate " + c + ": " + exc);
return null;
}
}
}
36 changes: 18 additions & 18 deletions src/main/java/org/bytedeco/javacpp/tools/Generator.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import org.bytedeco.javacpp.CharPointer;
import org.bytedeco.javacpp.ClassProperties;
import org.bytedeco.javacpp.DoublePointer;
import org.bytedeco.javacpp.EnumValueMap;
import org.bytedeco.javacpp.FloatPointer;
import org.bytedeco.javacpp.FunctionPointer;
import org.bytedeco.javacpp.IntPointer;
Expand Down Expand Up @@ -488,6 +489,7 @@ boolean classes(boolean handleExceptions, boolean defineAdapters, boolean conver
out.println("static jfieldID JavaCPP_shortValueFID = NULL;");
out.println("static jfieldID JavaCPP_intValueFID = NULL;");
out.println("static jfieldID JavaCPP_longValueFID = NULL;");
out.println("static jmethodID JavaCPP_getEnumMID = NULL;");
}
out.println("static jmethodID JavaCPP_initMID = NULL;");
out.println("static jmethodID JavaCPP_arrayMID = NULL;");
Expand Down Expand Up @@ -1735,6 +1737,13 @@ boolean classes(boolean handleExceptions, boolean defineAdapters, boolean conver
out.println(" if (JavaCPP_longValueFID == NULL) {");
out.println(" return JNI_ERR;");
out.println(" }");

out.println(" JavaCPP_getEnumMID = JavaCPP_getStaticMethodID(env, " +
jclasses.index(EnumValueMap.class) + ", \"getEnum\", \"(" + signature(long.class, Class.class) + ")"
+ signature(Enum.class) + "\");");
out.println(" if (JavaCPP_getEnumMID == NULL) {");
out.println(" return JNI_ERR;");
out.println(" }");
}
out.println(" JavaCPP_initMID = JavaCPP_getMethodID(env, " +
jclasses.index(Pointer.class) + ", \"init\", \"(JJJJ)V\");");
Expand Down Expand Up @@ -2304,7 +2313,7 @@ String returnBefore(MethodInformation methodInfo) {
}
} else if (Enum.class.isAssignableFrom(methodInfo.returnType)) {
accessesEnums = true;
out.println(" jobject rarg = JavaCPP_createPointer(env, " + jclasses.index(methodInfo.returnType) + ");");
out.println(" jobject rarg = NULL;");
returnPrefix = typeName[0] + " rval" + typeName[1] + " = " + cast;
} else {
String valueTypeName = valueTypeName(typeName);
Expand Down Expand Up @@ -2722,13 +2731,9 @@ void returnAfter(MethodInformation methodInfo) {
out.println(indent + "rarg = rptr;");
} else if (Enum.class.isAssignableFrom(methodInfo.returnType)) {
accessesEnums = true;
String s = enumValueType(methodInfo.returnType);
if (s != null) {
String S = Character.toUpperCase(s.charAt(0)) + s.substring(1);
out.println(indent + "if (rarg != NULL) {");
out.println(indent + " env->Set" + S + "Field(rarg, JavaCPP_" + s + "ValueFID, (j" + s + ")rval);");
out.println(indent + "}");
}
out.println(indent + "rarg = env->CallStaticObjectMethod(JavaCPP_getClass(env, " +
jclasses.index(EnumValueMap.class) + "), JavaCPP_getEnumMID, (jlong)rval, JavaCPP_classes[" +
jclasses.index(methodInfo.returnType) + "]);");
} else {
boolean needInit = false;
if (adapterInfo != null) {
Expand Down Expand Up @@ -3088,15 +3093,10 @@ void callback(Class<?> cls, Method callbackMethod, String callbackName, int allo
(passBy instanceof ByPtr || passBy instanceof ByPtrRef ? "*arg" : "arg") + j + ";");
} else if (Enum.class.isAssignableFrom(callbackParameterTypes[j])) {
accessesEnums = true;
String s = enumValueType(callbackParameterTypes[j]);
if (s != null) {
String S = Character.toUpperCase(s.charAt(0)) + s.substring(1);
out.println(" jobject obj" + j + " = JavaCPP_createPointer(env, " + jclasses.index(callbackParameterTypes[j]) + ");");
out.println(" args[" + j + "].l = obj" + j + ";");
out.println(" if (obj" + j + " != NULL) {");
out.println(" env->Set" + S + "Field(obj" + j + ", JavaCPP_" + s + "ValueFID, (j" + s + ")arg" + j + ");");
out.println(" }");
}
out.println(" jobject obj" + j + " = env->CallStaticObjectMethod(JavaCPP_getClass(env, " +
jclasses.index(EnumValueMap.class) + "), JavaCPP_getEnumMID, (jlong)arg" + j + ", JavaCPP_classes[" +
jclasses.index(callbackParameterTypes[j]) + "]);");
out.println(" args[" + j + "].l = obj" + j + ";");
} else {
String[] typeName = cppTypeName(callbackParameterTypes[j], callbackParameterAnnotations[j]);
String valueTypeName = valueTypeName(typeName);
Expand Down Expand Up @@ -3917,7 +3917,7 @@ Annotation behavior(Annotation ... annotations) {

String enumValueType(Class<?> type) {
try {
Field f = type.getField("value");
Field f = type.getDeclaredField("value");
if (!f.getType().isPrimitive()) {
logger.warn("Field \"value\" of enum type \"" + type.getCanonicalName()
+ "\" is not of a primitive type. Compilation will most likely fail.");
Expand Down
2 changes: 2 additions & 0 deletions src/test/java/org/bytedeco/javacpp/EnumTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ public LongEnum call(ByteEnum e) {
}
}

public static native ByteEnum identity(ByteEnum e);
public static native BoolEnum Char2Bool(ByteEnum e);
public static native ShortEnum Char2Short(ByteEnum e);
public static native LongEnum Int2Long(IntEnum e);
Expand All @@ -106,6 +107,7 @@ public LongEnum call(ByteEnum e) {
assertEquals(123, Char2Short(ByteEnum.BYTE).value);
assertEquals(789, Int2Long(IntEnum.INT).value);
assertEquals(101112, enumCallback(new EnumCallback()).value);
assert(identity(ByteEnum.BYTE) == identity(ByteEnum.BYTE));

try {
Int2Long(null);
Expand Down
4 changes: 4 additions & 0 deletions src/test/resources/org/bytedeco/javacpp/EnumTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@ LongEnum Int2Long(IntEnum e) {
LongEnum enumCallback(LongEnum (*f)(CharEnum e)) {
return f(CharEnum::CHAR_ENUM);
}

CharEnum identity(CharEnum e) {
return e;
}

0 comments on commit 59c1dfe

Please sign in to comment.