From c8d4e55095749dbcbe88256cde9ce420a1d9a05d Mon Sep 17 00:00:00 2001 From: Kustaa Nyholm Date: Wed, 9 Feb 2022 08:33:27 +0200 Subject: [PATCH 1/3] Fix index out of bounds error --- src/main/java/org/bytedeco/javacpp/tools/Parser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/bytedeco/javacpp/tools/Parser.java b/src/main/java/org/bytedeco/javacpp/tools/Parser.java index f41309a0..a16c0737 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Parser.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Parser.java @@ -671,7 +671,7 @@ Type type(Context context, boolean definition) throws ParserException { } type.cppName += separator; Info info = infoMap.getFirst(t.cppName); - String s = info != null && info.cppTypes != null ? info.cppTypes[0] : t.cppName; + String s = info != null && info.cppTypes != null && info.cppTypes.length>0? info.cppTypes[0] : t.cppName; if (t.constValue && !s.startsWith("const ")) { s = "const " + s; } From 5134257f98e5ea0d0be6231e197a9697e5ff9c71 Mon Sep 17 00:00:00 2001 From: Kustaa Nyholm Date: Wed, 9 Feb 2022 08:34:52 +0200 Subject: [PATCH 2/3] First attempt at generated auto downcast based on c++ type_index --- .../bytedeco/javacpp/annotation/Downcast.java | 14 ++ .../org/bytedeco/javacpp/tools/Generator.java | 144 +++++++++++++++++- 2 files changed, 155 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/bytedeco/javacpp/annotation/Downcast.java diff --git a/src/main/java/org/bytedeco/javacpp/annotation/Downcast.java b/src/main/java/org/bytedeco/javacpp/annotation/Downcast.java new file mode 100644 index 00000000..4d66b2b6 --- /dev/null +++ b/src/main/java/org/bytedeco/javacpp/annotation/Downcast.java @@ -0,0 +1,14 @@ +package org.bytedeco.javacpp.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER }) +public @interface Downcast { + String base() default ""; + + boolean classcache() default false; +} diff --git a/src/main/java/org/bytedeco/javacpp/tools/Generator.java b/src/main/java/org/bytedeco/javacpp/tools/Generator.java index b46c7b15..2c831a27 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Generator.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Generator.java @@ -93,6 +93,8 @@ import org.bytedeco.javacpp.annotation.ValueSetter; import org.bytedeco.javacpp.annotation.Virtual; import sun.misc.Unsafe; +import java.lang.invoke.MethodHandleInfo; +import org.bytedeco.javacpp.annotation.Downcast; /** * The Generator is where all the C++ source code that we need gets generated. @@ -181,6 +183,124 @@ static enum LongEnum { LONG; long value; } Map annotationCache; boolean mayThrowExceptions, usesAdapters, passesStrings, accessesEnums; + private void outputDowncastFunDeclaration() { + out.println(""); // a forward declaration is necessary because generated JNI methods will need to call the downcast function + out.println("extern \"C++\" template jobject JavaCPP_downcast(JNIEnv* env, BASE* obj);"); + out.println(""); +} + +private void outputDowncastFunDefinition() { + out.println(""); + out.println("#define JAVACPP_DEBUG_DCAST false"); + out.println(""); + out.println("#include "); + out.println("#include "); + out.println("#include "); + out.println("#include "); + out.println("#include "); + out.println("#include "); + out.println(""); + out.println("typedef struct constructor {"); + out.println(" jclass clazz;"); + out.println(" jmethodID ctor_mid;"); + out.println("} constructor;"); + out.println(""); + out.println("typedef std::unordered_map JavaCPP_JavaCPP_downcast_cache_t;"); + out.println(""); + out.println("JavaCPP_JavaCPP_downcast_cache_t JavaCPP_downcast_cache;"); + out.println(""); + out.println("extern \"C++\" template jobject JavaCPP_downcast(JNIEnv* env, BASE* obj) {"); + out.println(" std::type_index id = typeid(*obj);"); + out.println(" JavaCPP_JavaCPP_downcast_cache_t::iterator ctor = JavaCPP_downcast_cache.find(id);"); + out.println(" if (ctor != JavaCPP_downcast_cache.end()) {"); + out.println(" jobject ret = (jobject)env->NewObject(ctor->second.clazz, ctor->second.ctor_mid, NULL);"); + out.println(" if (ret == NULL || env->ExceptionCheck()) {"); + out.println(" std::cerr << \"Error calling Pointer constructor \" << std::endl;"); + out.println(" return NULL;"); + out.println(" }"); + out.println(" if (JAVACPP_DEBUG_DCAST) std::cerr << \"Success! \" << std::endl;"); + out.println(" return ret;"); + out.println(" }"); + out.println(" else"); + out.println(" return NULL;"); + out.println("}"); + out.println(""); + out.println("void JavaCPP_dcast_preload(JNIEnv* env, std::string class_name, std::type_index type_id) {"); + out.println(" if (JAVACPP_DEBUG_DCAST) std::cerr << \"Try to find class \" << class_name << std::endl;"); + out.println(""); + out.println(" jclass lclazz = env->FindClass(class_name.c_str());"); + out.println(" if (lclazz == NULL || env->ExceptionCheck()) {"); + out.println(" std::cerr << \"Error loading class \" << class_name.c_str() << std::endl;"); + out.println(" return;"); + out.println(" }"); + out.println(""); + out.println(" jclass gclazz = (jclass)env->NewGlobalRef(lclazz);"); + out.println(" if (gclazz == NULL || env->ExceptionCheck()) {"); + out.println(" std::cerr << \"Error creating class \" << class_name << std::endl;"); + out.println(" return;"); + out.println(" }"); + out.println(" "); + out.println(" if (JAVACPP_DEBUG_DCAST) std::cerr << \"Try to call constructor for class \" << class_name << std::endl;"); + out.println(" jmethodID ctor_mid = env->GetMethodID(gclazz, \"\", \"(Lorg/bytedeco/javacpp/Pointer;)V\");"); + out.println(" if (ctor_mid == NULL || env->ExceptionCheck()) {"); + out.println(" std::cerr << \"Error getting Pointer constructor \" << class_name << std::endl;"); + out.println(" return;"); + out.println(" }"); + out.println(" JavaCPP_downcast_cache[type_id] = constructor{gclazz,ctor_mid};"); + out.println(" }"); +} + +void outputDowncastCache(LinkedHashSet allClasses) { + var baseClasses = new LinkedHashSet(); + // Scan to find out Downcast base classes + for (Class cls : allClasses) { + // if super class is InfoMapper then this is likely a target class else skip this + if (!InfoMapper.class.isAssignableFrom(cls.getSuperclass())) + continue; + // check all methods to see if they declare a Downcast baseclass via annotation + for (Method m : cls.getMethods()) { + Downcast d = m.getAnnotation(Downcast.class); + if (d != null && d.base().length() > 0) + baseClasses.add(d.base()); + } + } + + if (!baseClasses.isEmpty()) + outputDowncastFunDefinition(); + + out.println("static void JavaCPP_dcast_preload_classes(JNIEnv* env) {"); + // Create/output runtime cache initialisation code for all target classes that are derived from any of the baseclasses + for (Class c : allClasses) { + // if super class is InfoMapper then this is likely a target class else skip this + if (!InfoMapper.class.isAssignableFrom(c.getSuperclass())) + continue; + + for (Class c2 : c.getClasses()) { + if (c2.isInterface()) + continue; + // follow up the super class chain to see if this is derived from a baseclass + var sc = c2; + while (sc != null) { + if (baseClasses.contains(sc.getSimpleName())) + break; + sc = sc.getSuperclass(); + } + if (sc == null) { + continue; + } + + String canonName = c2.getCanonicalName(); + int i = canonName.lastIndexOf("."); + String javaName = (canonName.substring(0, i) + "$" + canonName.substring(i + 1)).replaceAll("\\.", "/"); + Name na = c2.getAnnotation(Name.class); + String cppTypeName = na != null ? na.value()[0] : c2.getSimpleName(); + out.println(" JavaCPP_dcast_preload(env, \"" + javaName + "\", typeid(::" + cppTypeName + "));"); + } + } + out.println(" }"); + +} + public boolean generate(String sourceFilename, String jniConfigFilename, String reflectConfigFilename, String headerFilename, String loadSuffix, String baseLoadSuffix, String classPath, Class ... classes) throws IOException { try { @@ -1646,6 +1766,7 @@ boolean classes(boolean handleExceptions, boolean defineAdapters, boolean conver out.println(); out.println("JNIEXPORT jint JNICALL JNI_OnLoad" + baseLoadSuffix + "(JavaVM* vm, void* reserved);"); out.println("JNIEXPORT void JNICALL JNI_OnUnload" + baseLoadSuffix + "(JavaVM* vm, void* reserved);"); + out.println("static void JavaCPP_dcast_preload_classes(JNIEnv* env);"); } out.println(); // XXX: JNI_OnLoad() should ideally be protected by some mutex out.println("JNIEXPORT jint JNICALL JNI_OnLoad" + loadSuffix + "(JavaVM* vm, void* reserved) {"); @@ -1691,6 +1812,8 @@ boolean classes(boolean handleExceptions, boolean defineAdapters, boolean conver out.println(" env->PopLocalFrame(NULL);"); out.println(" }"); out.println(" }"); + if (baseLoadSuffix != null && !baseLoadSuffix.isEmpty()) + out.println(" JavaCPP_dcast_preload_classes(env);"); out.println(" }"); out.println(" JavaCPP_addressFID = JavaCPP_getFieldID(env, " + jclasses.index(Pointer.class) + ", \"address\", \"J\");"); @@ -1862,7 +1985,9 @@ boolean classes(boolean handleExceptions, boolean defineAdapters, boolean conver out.println(" JavaCPP_vm = NULL;"); out.println("}"); out.println(); - + + outputDowncastFunDeclaration(); + boolean supportedPlatform = false; LinkedHashSet allClasses = new LinkedHashSet(); if (baseLoadSuffix == null || baseLoadSuffix.isEmpty()) { @@ -1889,6 +2014,9 @@ boolean classes(boolean handleExceptions, boolean defineAdapters, boolean conver out.println("}"); out.println(); + + outputDowncastCache(allClasses); + if (out2 != null) { out2.println("#ifdef __cplusplus"); out2.println("}"); @@ -2325,7 +2453,10 @@ String returnBefore(MethodInformation methodInfo) { out.println(" jobject rarg = obj;"); } else if (methodInfo.returnType.isPrimitive()) { out.println(" " + jniTypeName(methodInfo.returnType) + " rarg = 0;"); - returnPrefix = typeName[0] + " rval" + typeName[1] + " = " + cast; + if (typeName.length >= 2) + returnPrefix = typeName[0] + " rval" + typeName[1] + " = " + cast; + else + returnPrefix = typeName[0] + " rval" + " = " + cast; if ((returnBy instanceof ByPtr) || (returnBy instanceof ByPtrRef)) { returnPrefix += "*"; } @@ -2818,7 +2949,14 @@ void returnAfter(MethodInformation methodInfo) { } } out.println( "if (rptr != NULL) {"); - out.println(indent + " rarg = JavaCPP_createPointer(env, " + jclasses.index(methodInfo.returnType) + + + Downcast a = (Downcast) methodInfo.method.getAnnotation(Downcast.class); + if (a != null) + out.println(indent + " rarg = JavaCPP_downcast<" + a.base() + ">(env, rptr);"); + else + out.println(indent + " rarg = JavaCPP_createPointer(env, " + jclasses.index(methodInfo.returnType) + (methodInfo.parameterTypes.length > 0 && methodInfo.parameterTypes[0] == Class.class ? ", arg0);" : ");")); + + out.println(indent + " rarg = JavaCPP_createPointer(env, " + jclasses.index(methodInfo.returnType) + (methodInfo.parameterTypes.length > 0 && methodInfo.parameterTypes[0] == Class.class ? ", arg0);" : ");")); out.println(indent + " if (rarg != NULL) {"); if (needInit) { From a570b774babf0c3030b87a555f30e232a19c1a22 Mon Sep 17 00:00:00 2001 From: Kustaa Nyholm Date: Sat, 12 Feb 2022 08:52:58 +0200 Subject: [PATCH 3/3] Improved Java 1.7 compatibility by not using 'var' --- src/main/java/org/bytedeco/javacpp/tools/Generator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/bytedeco/javacpp/tools/Generator.java b/src/main/java/org/bytedeco/javacpp/tools/Generator.java index 2c831a27..b063a0ca 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Generator.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Generator.java @@ -251,7 +251,7 @@ private void outputDowncastFunDefinition() { } void outputDowncastCache(LinkedHashSet allClasses) { - var baseClasses = new LinkedHashSet(); + LinkedHashSet baseClasses = new LinkedHashSet(); // Scan to find out Downcast base classes for (Class cls : allClasses) { // if super class is InfoMapper then this is likely a target class else skip this @@ -279,7 +279,7 @@ void outputDowncastCache(LinkedHashSet allClasses) { if (c2.isInterface()) continue; // follow up the super class chain to see if this is derived from a baseclass - var sc = c2; + Class sc = c2; while (sc != null) { if (baseClasses.contains(sc.getSimpleName())) break;