diff --git a/CHANGELOG.md b/CHANGELOG.md index df64ceac4..e7c34381c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ + * Support multiple instances of `FunctionPointer` subclasses, up to the value in `@Allocator(max=...)` ([issue bytedeco/javacpp-presets#683](https://github.com/bytedeco/javacpp-presets/issues/683)) * Allow suffixing library names with `:` to specify exact relative paths to libraries, ignoring any additional prefix or suffix * Prevent `Loader.load()` from trying to load library files that do not exist or to create symbolic links to them * Let `Loader.load()` extract libraries suffixed with `##`, but still ignored for copying by `Builder` diff --git a/README.md b/README.md index 4486a6ee2..465e378dc 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ JavaCPP ======= -[![Join the chat at https://gitter.im/bytedeco/javacpp](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/bytedeco/javacpp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.bytedeco/javacpp/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.bytedeco/javacpp) [![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/https/oss.sonatype.org/org.bytedeco/javacpp.svg)](http://bytedeco.org/builds/) [![Build Status](https://travis-ci.org/bytedeco/javacpp.svg?branch=master)](https://travis-ci.org/bytedeco/javacpp) +[![Gitter](https://badges.gitter.im/bytedeco/javacpp.svg)](https://gitter.im/bytedeco/javacpp) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.bytedeco/javacpp/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.bytedeco/javacpp) [![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/https/oss.sonatype.org/org.bytedeco/javacpp.svg)](http://bytedeco.org/builds/) [![Build Status](https://travis-ci.org/bytedeco/javacpp.svg?branch=master)](https://travis-ci.org/bytedeco/javacpp) Introduction diff --git a/src/main/java/org/bytedeco/javacpp/annotation/Allocator.java b/src/main/java/org/bytedeco/javacpp/annotation/Allocator.java index d65b3054f..9ec723d30 100644 --- a/src/main/java/org/bytedeco/javacpp/annotation/Allocator.java +++ b/src/main/java/org/bytedeco/javacpp/annotation/Allocator.java @@ -5,6 +5,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.bytedeco.javacpp.FunctionPointer; import org.bytedeco.javacpp.Pointer; import org.bytedeco.javacpp.tools.Generator; @@ -20,6 +21,8 @@ * the given arguments, and initializes the {@link Pointer#address} as well as * the {@link Pointer#deallocator} with {@code NativeDeallocator}, based on the * {@code delete} operator, if not additionally annotated with {@link NoDeallocator}. + *

+ * Can also be used on classes to set the {@link #max} value for enclosed function pointers. * * @see Pointer#init(long, long, long, long) * @see Generator @@ -27,5 +30,9 @@ * @author Samuel Audet */ @Documented @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD}) -public @interface Allocator { } +@Target({ElementType.TYPE, ElementType.METHOD}) +public @interface Allocator { + /** The maximum number of instances that can be allocated in the case of a {@link FunctionPointer} subclass. + * Does not affect the underlying function object or other {@link Pointer} which have no such allocation limits. */ + int max() default 10; +} diff --git a/src/main/java/org/bytedeco/javacpp/tools/Generator.java b/src/main/java/org/bytedeco/javacpp/tools/Generator.java index 61bff5519..94ef7fde8 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Generator.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Generator.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2018 Samuel Audet + * Copyright (C) 2011-2019 Samuel Audet * * Licensed either under the Apache License, Version 2.0, or (at your option) * under the terms of the GNU General Public License as published by @@ -39,8 +39,8 @@ import java.nio.LongBuffer; import java.nio.ShortBuffer; import java.util.Arrays; -import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -191,10 +191,10 @@ public boolean generate(String sourceFilename, String headerFilename, String loa deallocators = new IndexedSet(); arrayDeallocators = new IndexedSet(); jclasses = new IndexedSet(); - members = new HashMap>(); - virtualFunctions = new HashMap>(); - virtualMembers = new HashMap>(); - annotationCache = new HashMap(); + members = new LinkedHashMap>(); + virtualFunctions = new LinkedHashMap>(); + virtualMembers = new LinkedHashMap>(); + annotationCache = new LinkedHashMap(); mayThrowExceptions = false; usesAdapters = false; passesStrings = false; @@ -1327,9 +1327,12 @@ boolean classes(boolean handleExceptions, boolean defineAdapters, boolean conver String name = "JavaCPP_" + mangle(c.getName()); out.print("static void " + name + "_deallocate(void *p) { "); if (FunctionPointer.class.isAssignableFrom(c)) { - String typeName = functionClassName(c) + "*"; - out.println("JNIEnv *e; bool a = JavaCPP_getEnv(&e); if (e != NULL) e->DeleteWeakGlobalRef((jweak)((" - + typeName + ")p)->obj); delete (" + typeName + ")p; JavaCPP_detach(a); }"); + String typeName = functionClassName(c); + out.println("\n int n = sizeof(" + typeName + "_instances) / sizeof(" + typeName + "_instances[0]);" + + "\n for (int i = 0; i < n; i++) { if (" + typeName + "_instances[i].obj == ((" + + typeName + "*)p)->obj) " + typeName + "_instances[i].obj = NULL; }" + + "\n JNIEnv *e; bool a = JavaCPP_getEnv(&e); if (e != NULL) e->DeleteWeakGlobalRef((jweak)((" + + typeName + "*)p)->obj); delete (" + typeName + "*)p; JavaCPP_detach(a); }"); } else if (virtualFunctions.containsKey(c)) { String[] typeName = cppTypeName(c); String valueTypeName = valueTypeName(typeName); @@ -1347,7 +1350,7 @@ boolean classes(boolean handleExceptions, boolean defineAdapters, boolean conver out.println("static void " + name + "_deallocateArray(void* p) { delete[] (" + typeName[0] + typeName[1] + ")p; }"); } out.println(); - out.println("static const char* JavaCPP_members[" + jclasses.size() + "][" + maxMemberSize + 1 + "] = {"); + out.println("static const char* JavaCPP_members[" + jclasses.size() + "][" + (maxMemberSize + 1) + "] = {"); classIterator = jclasses.iterator(); while (classIterator.hasNext()) { out.print(" { "); @@ -1367,7 +1370,7 @@ boolean classes(boolean handleExceptions, boolean defineAdapters, boolean conver } } out.println(" };"); - out.println("static int JavaCPP_offsets[" + jclasses.size() + "][" + maxMemberSize + 1 + "] = {"); + out.println("static int JavaCPP_offsets[" + jclasses.size() + "][" + (maxMemberSize + 1) + "] = {"); classIterator = jclasses.iterator(); while (classIterator.hasNext()) { out.print(" { "); @@ -1733,7 +1736,7 @@ boolean methods(Class cls) { if (name != null && name.value().length > 0 && name.value()[0].length() > 0) { callbackName = name.value()[0]; } - callback(cls, functionMethod, callbackName, firstCallback, null); + callback(cls, functionMethod, callbackName, methodInfo.allocatorMax, firstCallback, null); firstCallback = false; didSomething = true; } @@ -1743,7 +1746,8 @@ boolean methods(Class cls) { if ((Modifier.isNative(methods[i].getModifiers()) || Modifier.isAbstract(methods[i].getModifiers())) && !methodInfo.valueGetter && !methodInfo.valueSetter && !methodInfo.memberGetter && !methodInfo.memberSetter && !cls.isInterface() && (methods[i].isAnnotationPresent(Virtual.class) || methodInfo.allocator)) { - callback(cls, methods[i], methodInfo.memberName[0], !methodInfo.allocator, methodInfo); + // also process virtual methods and their allocators as callbacks + callback(cls, methods[i], methodInfo.memberName[0], methodInfo.allocatorMax, !methodInfo.allocator, methodInfo); } if (!Modifier.isNative(methods[i].getModifiers())) { @@ -1773,7 +1777,7 @@ boolean methods(Class cls) { out.println(") {"); if (callbackAllocators[i]) { - callbackAllocator(cls, callbackName); + callbackAllocator(cls, callbackName, methodInfo.allocatorMax); continue; } else if (!Modifier.isStatic(methodInfo.modifiers) && Pointer.class.isAssignableFrom(cls) && !methodInfo.allocator && !methodInfo.arrayAllocator && !methodInfo.deallocator) { @@ -1793,7 +1797,7 @@ boolean methods(Class cls) { jclasses.index(NullPointerException.class) + "), \"This pointer address is NULL.\");"); out.println(" return" + (methodInfo.returnType == void.class ? ";" : " 0;")); out.println(" }"); - if (FunctionPointer.class.isAssignableFrom(cls)) { + if (FunctionPointer.class.isAssignableFrom(cls) && !methodInfo.valueGetter && !methodInfo.valueSetter) { out.println(" if (ptr->ptr == NULL) {"); out.println(" env->ThrowNew(JavaCPP_getClass(env, " + jclasses.index(NullPointerException.class) + "), \"This function pointer address is NULL.\");"); @@ -2616,7 +2620,7 @@ void parametersAfter(MethodInformation methodInfo) { } } - void callback(Class cls, Method callbackMethod, String callbackName, boolean needDefinition, MethodInformation methodInfo) { + void callback(Class cls, Method callbackMethod, String callbackName, int allocatorMax, boolean needDefinition, MethodInformation methodInfo) { Class callbackReturnType = callbackMethod.getReturnType(); Class[] callbackParameterTypes = callbackMethod.getParameterTypes(); Annotation[] callbackAnnotations = callbackMethod.getAnnotations(); @@ -2692,7 +2696,7 @@ void callback(Class cls, Method callbackMethod, String callbackName, boolean } memberList.add(member); } else if (callbackName != null) { - callbacks.index("static " + instanceTypeName + " " + callbackName + "_instance;"); + callbacks.index("static " + instanceTypeName + " " + instanceTypeName + "_instances[" + allocatorMax + "];"); Convention convention = cls.getAnnotation(Convention.class); if (convention != null && !convention.extern().equals("C")) { out.println("extern \"" + convention.extern() + "\" {"); @@ -2700,27 +2704,38 @@ void callback(Class cls, Method callbackMethod, String callbackName, boolean out2.println("extern \"" + convention.extern() + "\" {"); } } - if (out2 != null) { - out2.println("JNIIMPORT " + returnConvention[0] + (returnConvention.length > 1 ? - returnConvention[1] : "") + callbackName + parameterDeclaration + ";"); - } - out.println("JNIEXPORT " + returnConvention[0] + (returnConvention.length > 1 ? - returnConvention[1] : "") + callbackName + parameterDeclaration + " {"); - out.print((callbackReturnType != void.class ? " return " : " ") + callbackName + "_instance("); - for (int j = 0; j < callbackParameterTypes.length; j++) { - out.print("arg" + j); - if (j < callbackParameterTypes.length - 1) { - out.print(", "); + for (int i = 0; i < allocatorMax; i++) { + if (out2 != null) { + out2.println("JNIIMPORT " + returnConvention[0] + (returnConvention.length > 1 ? + returnConvention[1] : "") + callbackName + (i > 0 ? i : "") + parameterDeclaration + ";"); + } + out.println("JNIEXPORT " + returnConvention[0] + (returnConvention.length > 1 ? + returnConvention[1] : "") + callbackName + (i > 0 ? i : "") + parameterDeclaration + " {"); + out.print((callbackReturnType != void.class ? " return " : " ") + instanceTypeName + "_instances[" + i + "]("); + for (int j = 0; j < callbackParameterTypes.length; j++) { + out.print("arg" + j); + if (j < callbackParameterTypes.length - 1) { + out.print(", "); + } } + out.println(");"); + out.println("}"); } - out.println(");"); - out.println("}"); if (convention != null && !convention.extern().equals("C")) { out.println("}"); if (out2 != null) { out2.println("}"); } } + out.println("static " + returnConvention[0] + "(*" + (returnConvention.length > 1 ? + returnConvention[1] : "") + callbackName + "s[" + allocatorMax + "])" + parameterDeclaration + " = {"); + for (int i = 0; i < allocatorMax; i++) { + out.print(" " + callbackName + (i > 0 ? i : "")); + if (i + 1 < allocatorMax) { + out.println(","); + } + } + out.println(" };"); firstLine = returnConvention[0] + instanceTypeName + "::operator()" + parameterDeclaration + " {"; } @@ -2913,7 +2928,12 @@ void callback(Class cls, Method callbackMethod, String callbackName, boolean out.println(" } else {"); out.println(" env->SetLongField(obj, JavaCPP_addressFID, ptr_to_jlong(this));"); out.println(" }"); - out.println(" ptr = &" + callbackName + ";"); + out.println(" for (int i = 0; i < " + allocatorMax + "; i++) {"); + out.println(" if (this == &" + instanceTypeName + "_instances[i]) {"); + out.println(" ptr = " + callbackName + "s[i];"); + out.println(" break;"); + out.println(" }"); + out.println(" }"); out.println(" }"); out.println(" if (mid == NULL) {"); out.println(" mid = JavaCPP_getMethodID(env, " + jclasses.index(cls) + ", \"" + callbackMethod.getName() + "\", \"(" + @@ -3079,9 +3099,8 @@ void callback(Class cls, Method callbackMethod, String callbackName, boolean out.println("}"); } - void callbackAllocator(Class cls, String callbackName) { - // XXX: Here, we should actually allocate new trampolines on the heap somehow... - // For now it just bumps out from the global variable the last object that called this method + void callbackAllocator(Class cls, String callbackName, int allocatorMax) { + // XXX: Make callback function pointer allocation more thread safe String[] typeName = cppTypeName(cls); String instanceTypeName = functionClassName(cls); out.println(" obj = env->NewWeakGlobalRef(obj);"); @@ -3091,12 +3110,19 @@ void callbackAllocator(Class cls, String callbackName) { out.println(" }"); out.println(" " + instanceTypeName + "* rptr = new (std::nothrow) " + instanceTypeName + ";"); out.println(" if (rptr != NULL) {"); - out.println(" rptr->ptr = " + (callbackName == null ? "(" + typeName[0] + typeName[1] + ")jlong_to_ptr(arg0)" : "&" + callbackName) + ";"); out.println(" rptr->obj = obj;"); out.println(" JavaCPP_initPointer(env, obj, rptr, 1, rptr, &JavaCPP_" + mangle(cls.getName()) + "_deallocate);"); deallocators.index(cls); if (callbackName != null) { - out.println(" " + callbackName + "_instance = *rptr;"); + out.println(" for (int i = 0; i < " + allocatorMax + "; i++) {"); + out.println(" if (" + instanceTypeName + "_instances[i].obj == NULL) {"); + out.println(" rptr->ptr = " + callbackName + "s[i];"); + out.println(" " + instanceTypeName + "_instances[i] = *rptr;"); + out.println(" break;"); + out.println(" }"); + out.println(" }"); + } else { + out.println(" rptr->ptr = (" + typeName[0] + typeName[1] + ")jlong_to_ptr(arg0);"); } out.println(" }"); out.println("}"); @@ -3151,6 +3177,7 @@ MethodInformation methodInformation(Method method) { Name name = method.getAnnotation(Name.class); info.memberName = name != null ? name.value() : new String[] { info.name }; Index index = method.getAnnotation(Index.class); + info.allocatorMax = allocatorMax(info.cls, info.method); info.dim = index != null ? index.value() : 0; info.parameterTypes = method.getParameterTypes(); info.parameterAnnotations = method.getParameterAnnotations(); @@ -3355,6 +3382,25 @@ MethodInformation methodInformation(Method method) { return info; } + static int allocatorMax(Class cls, Method method) { + Allocator a = method.getAnnotation(Allocator.class); + while (a == null && cls != null) { + if ((a = cls.getAnnotation(Allocator.class)) != null) { + break; + } + if (cls.getEnclosingClass() != null) { + cls = cls.getEnclosingClass(); + } else { + cls = cls.getSuperclass(); + } + } + try { + return a != null ? a.max() : (int)Allocator.class.getDeclaredMethod("max").getDefaultValue(); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + static boolean criticalRegion(Class cls, Method method) { boolean criticalRegion = baseClasses.contains(cls) || method.isAnnotationPresent(CriticalRegion.class); @@ -3570,9 +3616,16 @@ static String constValueTypeName(String ... typeName) { static String valueTypeName(String ... typeName) { String type = typeName[0]; if (type.startsWith("const ")) { - type = type.substring(6, type.length()-1); - } else if (type.endsWith("*") || type.endsWith("&")) { - type = type.substring(0, type.length()-1); + type = type.substring(6); + } + if (type.endsWith(" const")) { + type = type.substring(0, type.length() - 6); + } + if (type.endsWith("*") || type.endsWith("&")) { + type = type.substring(0, type.length() - 1); + } + if (type.endsWith(" const")) { + type = type.substring(0, type.length() - 6); } return type; } @@ -3663,12 +3716,12 @@ String[] cppCastTypeName(Class type, Annotation ... annotations) { typeName = cppTypeName(type); if (typeName[0].contains("(*")) { // function pointer - if (b.length > 0 && b[0] && !typeName[0].endsWith("const")) { - typeName[0] += "const"; + if (b.length > 0 && b[0] && !typeName[0].endsWith(" const")) { + typeName[0] += " const"; } } else { - if (b.length > 1 && b[1] && !typeName[0].endsWith(" const *")) { - typeName[0] = valueTypeName(typeName) + " const *"; + if (b.length > 1 && b[1] && !typeName[0].endsWith(" const")) { + typeName[0] += " const"; } if (b.length > 0 && b[0] && !typeName[0].startsWith("const ")) { typeName[0] = "const " + typeName[0]; diff --git a/src/main/java/org/bytedeco/javacpp/tools/MethodInformation.java b/src/main/java/org/bytedeco/javacpp/tools/MethodInformation.java index f37029c66..e9557ba32 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/MethodInformation.java +++ b/src/main/java/org/bytedeco/javacpp/tools/MethodInformation.java @@ -36,7 +36,7 @@ public class MethodInformation { int modifiers; Class returnType; String name, memberName[]; - int dim; + int allocatorMax, dim; boolean[] parameterRaw; Class[] parameterTypes; Annotation[][] parameterAnnotations; diff --git a/src/main/java/org/bytedeco/javacpp/tools/Parser.java b/src/main/java/org/bytedeco/javacpp/tools/Parser.java index 2681b2356..e1b21ea9c 100644 --- a/src/main/java/org/bytedeco/javacpp/tools/Parser.java +++ b/src/main/java/org/bytedeco/javacpp/tools/Parser.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2018 Samuel Audet + * Copyright (C) 2013-2019 Samuel Audet * * Licensed either under the Apache License, Version 2.0, or (at your option) * under the terms of the GNU General Public License as published by @@ -207,9 +207,6 @@ void containers(Context context, DeclarationList declList) throws ParserExceptio if (valueType.constValue && !cast.startsWith("const ")) { cast = "const " + cast; } - if (valueType.constPointer && !cast.endsWith(" const")) { - cast = cast + " const"; - } if (valueType.indirections > 0) { for (int i = 0; i < valueType.indirections; i++) { cast += "*"; @@ -220,6 +217,9 @@ void containers(Context context, DeclarationList declList) throws ParserExceptio if (valueType.reference) { cast += "&"; } + if (valueType.constPointer && !cast.endsWith(" const")) { + cast = cast + " const"; + } valueType.annotations = "@Cast(\"" + cast + "\") " + valueType.annotations; } String arrayBrackets = ""; @@ -564,15 +564,15 @@ Type type(Context context, boolean definition) throws ParserException { if (t.constValue && !s.startsWith("const ")) { s = "const " + s; } - if (t.constPointer && !s.endsWith(" const")) { - s = s + " const"; - } for (int i = 0; i < t.indirections; i++) { s += "*"; } if (t.reference) { s += "&"; } + if (t.constPointer && !s.endsWith(" const")) { + s = s + " const"; + } type.cppName += s; separator = ","; } @@ -709,6 +709,10 @@ Type type(Context context, boolean definition) throws ParserException { type.constValue = true; type.cppName = type.cppName.substring(6); } + if (type.cppName.endsWith(" const")) { + type.constPointer = true; + type.cppName = type.cppName.substring(0, type.cppName.length() - 6); + } if (type.cppName.endsWith("*")) { type.indirections++; if (type.reference) { @@ -721,7 +725,7 @@ Type type(Context context, boolean definition) throws ParserException { type.cppName = type.cppName.substring(0, type.cppName.length() - 1); } if (type.cppName.endsWith(" const")) { - type.constPointer = true; + type.constValue = true; type.cppName = type.cppName.substring(0, type.cppName.length() - 6); } @@ -729,7 +733,8 @@ Type type(Context context, boolean definition) throws ParserException { String shortName = type.cppName; String[] names = context.qualify(type.cppName); if (definition && names.length > 0) { - String constName = type.constValue || type.constPointer ? "const " + names[0] : names[0]; + String constName = type.constValue ? "const " + names[0] : names[0]; + constName = type.constPointer ? constName + " const" : constName; info = infoMap.getFirst(constName, false); type.cppName = names[0]; } else { @@ -745,7 +750,8 @@ Type type(Context context, boolean definition) throws ParserException { // skip, we would probably get Info for the constructors, not the type continue; } - String constName = type.constValue || type.constPointer ? "const " + name : name; + String constName = type.constValue ? "const " + name : name; + constName = type.constPointer ? constName + " const" : constName; if ((info = infoMap.getFirst(constName, false)) != null) { type.cppName = name; break; @@ -765,6 +771,10 @@ Type type(Context context, boolean definition) throws ParserException { type.constValue = true; type.cppName = type.cppName.substring(6); } + if (type.cppName.endsWith(" const")) { + type.constPointer = true; + type.cppName = type.cppName.substring(0, type.cppName.length() - 6); + } if (type.cppName.endsWith("*")) { type.indirections++; if (type.reference) { @@ -777,7 +787,7 @@ Type type(Context context, boolean definition) throws ParserException { type.cppName = type.cppName.substring(0, type.cppName.length() - 1); } if (type.cppName.endsWith(" const")) { - type.constPointer = true; + type.constValue = true; type.cppName = type.cppName.substring(0, type.cppName.length() - 6); } @@ -1182,9 +1192,6 @@ Declarator declarator(Context context, String defaultName, int infoNumber, boole if (type.constValue && !cast.startsWith("const ")) { cast = "const " + cast; } - if (type.constPointer && !cast.endsWith(" const")) { - cast = cast + " const"; - } if (type.indirections > 0) { dcl.indirections += type.indirections; for (int i = 0; i < type.indirections; i++) { @@ -1195,6 +1202,9 @@ Declarator declarator(Context context, String defaultName, int infoNumber, boole dcl.reference = true; cast += "&"; } + if (type.constPointer && !cast.endsWith(" const")) { + cast = cast + " const"; + } for (String s : info2.annotations) { type.annotations += s + " "; } @@ -1245,10 +1255,12 @@ Declarator declarator(Context context, String defaultName, int infoNumber, boole } if (!needCast && !type.javaName.contains("@Cast")) { - if (type.constValue && !implicitConst && !type.constPointer) { + if (type.constValue && !implicitConst) { type.annotations = "@Const " + type.annotations; - } else if (type.constPointer) { - type.annotations = "@Const({" + type.constValue + ", " + type.constPointer + "}) " + type.annotations; + } + if (type.constPointer) { + // ignore, const pointers are not useful in generated code + // type.annotations = "@Const({" + type.constValue + ", " + type.constPointer + "}) " + type.annotations; } } } @@ -1371,6 +1383,13 @@ Declarator declarator(Context context, String defaultName, int infoNumber, boole functionType = functionType.substring(functionType.lastIndexOf(' ') + 1); // get rid of pointer annotations if (!functionType.equals("Pointer")) { definition.type = new Type(functionType); + for (Info info2 : infoMap.get("function/pointers")) { + if (info2 != null && info2.annotations != null) { + for (String s : info2.annotations) { + definition.text += s + " "; + } + } + } definition.text += (tokens.get().match(Token.CONST, Token.__CONST, Token.CONSTEXPR) ? "@Const " : "") + "public static class " + functionType + " extends FunctionPointer {\n" + " static { Loader.load(); }\n" + @@ -1923,9 +1942,6 @@ boolean function(Context context, DeclarationList declList) throws ParserExcepti if (d.type.constValue && !s.startsWith("const ")) { s = "const " + s; } - if (d.type.constPointer && !s.endsWith(" const")) { - s = s + " const"; - } if (d.indirections > 0) { for (int i = 0; i < d.indirections; i++) { s += "*"; @@ -1936,6 +1952,9 @@ boolean function(Context context, DeclarationList declList) throws ParserExcepti s += "&"; s2 += "&"; } + if (d.type.constPointer && !s.endsWith(" const")) { + s = s + " const"; + } fullname += separator + s; fullname2 += separator + s2; separator = ", "; @@ -2264,12 +2283,12 @@ boolean variable(Context context, DeclarationList declList) throws ParserExcepti dcl.type.annotations = dcl.type.annotations.replaceAll("@Name\\(.*\\) ", ""); javaName = metadcl.javaName + "_" + shortName; } - if (dcl.type.constValue || dcl.constPointer) { + if ((dcl.type.constValue && dcl.indirections == 0) || dcl.constPointer) { decl.text += "@MemberGetter "; } decl.text += modifiers + dcl.type.annotations.replace("@ByVal ", "@ByRef ") + dcl.type.javaName + " " + javaName + "(" + indices + ");"; - if (!dcl.type.constValue && !dcl.constPointer) { + if (!(dcl.type.constValue && dcl.indirections == 0) && !dcl.constPointer) { if (indices.length() > 0) { indices += ", "; } @@ -2650,9 +2669,6 @@ boolean typedef(Context context, DeclarationList declList) throws ParserExceptio if (dcl.type.constValue && !s.startsWith("const ")) { s = "const " + s; } - if (dcl.type.constPointer && !s.endsWith(" const")) { - s = s + " const"; - } if (dcl.type.indirections > 0) { for (int i = 0; i < dcl.type.indirections; i++) { s += "*"; @@ -2661,6 +2677,9 @@ boolean typedef(Context context, DeclarationList declList) throws ParserExceptio if (dcl.type.reference) { s += "&"; } + if (dcl.type.constPointer && !s.endsWith(" const")) { + s = s + " const"; + } info.cppNames(defName, s).cppTypes(s); } if (info.valueTypes == null && dcl.indirections > 0) { @@ -3498,9 +3517,6 @@ void declarations(Context context, DeclarationList declList) throws ParserExcept if (t.constValue && !s.startsWith("const ")) { s = "const " + s; } - if (t.constPointer && !s.endsWith(" const")) { - s = s + " const"; - } if (t.indirections > 0) { for (int i = 0; i < t.indirections; i++) { s += "*"; @@ -3509,6 +3525,9 @@ void declarations(Context context, DeclarationList declList) throws ParserExcept if (t.reference) { s += "&"; } + if (t.constPointer && !s.endsWith(" const")) { + s = s + " const"; + } t.cppName = s; e.setValue(t); } diff --git a/src/test/java/org/bytedeco/javacpp/PointerTest.java b/src/test/java/org/bytedeco/javacpp/PointerTest.java index 70935b639..683d9dcb1 100644 --- a/src/test/java/org/bytedeco/javacpp/PointerTest.java +++ b/src/test/java/org/bytedeco/javacpp/PointerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016-2018 Samuel Audet + * Copyright (C) 2016-2019 Samuel Audet * * Licensed either under the Apache License, Version 2.0, or (at your option) * under the terms of the GNU General Public License as published by @@ -33,6 +33,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import org.bytedeco.javacpp.annotation.Allocator; import org.bytedeco.javacpp.annotation.Platform; import org.bytedeco.javacpp.tools.Builder; import org.junit.BeforeClass; @@ -47,8 +48,10 @@ @Platform(define = {"NATIVE_ALLOCATOR malloc", "NATIVE_DEALLOCATOR free"}) public class PointerTest { - static long maxBytes = 1024 * 1024 * 1024; /* 1g */ + static final int allocatorMax = 11; + static final long maxBytes = 1024 * 1024 * 1024; /* 1g */ + @Allocator(max = allocatorMax) static class TestFunction extends FunctionPointer { public TestFunction(Pointer p) { super(p); } public TestFunction() { allocate(); } @@ -67,12 +70,6 @@ static class TestFunction extends FunctionPointer { System.out.println("Loader"); Loader.load(c); - Pointer address = Loader.addressof("strlen"); - assertNotNull(address); - TestFunction function = new TestFunction().put(address); - assertEquals(address, function.get()); - assertEquals(5, function.call("12345")); - int totalProcessors = Loader.totalProcessors(); int totalCores = Loader.totalCores(); int totalChips = Loader.totalChips(); @@ -84,6 +81,45 @@ static class TestFunction extends FunctionPointer { assertNotEquals(null, Loader.getJavaVM()); } + @Test public void testFunctionPointer() { + System.out.println("FunctionPointer"); + + Pointer address = Loader.addressof("strlen"); + assertNotNull(address); + TestFunction function = new TestFunction().put(address); + assertEquals(address, function.get()); + assertEquals(5, function.call("12345")); + function.deallocate(); + + TestFunction[] functions = new TestFunction[allocatorMax]; + Pointer prevp = new Pointer(); + for (int i = 0; i < allocatorMax; i++) { + final int n = i; + functions[i] = new TestFunction() { + @Override public int call(String s) { return n; } + }; + Pointer p = functions[i].get(); + System.out.println(p); + assertNotNull(p); + assertNotEquals(prevp, p); + prevp = p; + } + + TestFunction f = new TestFunction() { + @Override public int call(String s) { return allocatorMax; } + }; + assertNull(f.get()); + + for (int i = 0; i < allocatorMax; i++) { + functions[i].deallocate(); + } + + TestFunction f2 = new TestFunction() { + @Override public int call(String s) { return allocatorMax; } + }; + assertNotNull(f2.get()); + } + static Object fieldReference; @Test public void testPointer() {