Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add @Utf16String annotation to map java.lang.String to unsigned short* #442

Merged
merged 2 commits into from
Feb 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

* Add `@AsUtf16` annotation to map `java.lang.String` to `unsigned short*` (array of UTF-16 code units) ([pull #442](https://github.com/bytedeco/javacpp/pull/#442))
* Add `BasicStringAdapter` and corresponding `@StdBasicString`, `@StdU16String`, and `@StdU32String` annotations ([pull #448](https://github.com/bytedeco/javacpp/pull/448))
* Fix `Parser` failures on `try` blocks as function body, nested class templates, and aliases to namespaces starting with `::`
* Prevent `Loader` from failing to find, load, or link libraries multiple times
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/org/bytedeco/javacpp/annotation/AsUtf16.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.bytedeco.javacpp.annotation;

import org.bytedeco.javacpp.tools.Generator;

import java.lang.annotation.*;

/**
* Indicates that {@link java.lang.String} should be mapped to array of UTF-16
* code units ({@code unsigned short*}) instead of byte array ({@code const char*}).
*
* @see Generator
*
* @author Alexey Rochev
*/

@Documented @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.PARAMETER})
public @interface AsUtf16 { }
124 changes: 102 additions & 22 deletions src/main/java/org/bytedeco/javacpp/tools/Generator.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
import org.bytedeco.javacpp.annotation.MemberSetter;
import org.bytedeco.javacpp.annotation.Name;
import org.bytedeco.javacpp.annotation.Namespace;
import org.bytedeco.javacpp.annotation.AsUtf16;
import org.bytedeco.javacpp.annotation.NoDeallocator;
import org.bytedeco.javacpp.annotation.NoException;
import org.bytedeco.javacpp.annotation.NoOffset;
Expand Down Expand Up @@ -937,20 +938,41 @@ boolean classes(boolean handleExceptions, boolean defineAdapters, boolean conver
out.println("}");
out.println();
if (handleExceptions || convertStrings) {
out.println("static JavaCPP_noinline jstring JavaCPP_createString(JNIEnv* env, const char* ptr) {");
out.println("#include <string>");
out.println("static JavaCPP_noinline jstring JavaCPP_createStringFromBytes(JNIEnv* env, const char* ptr, size_t length) {");
out.println(" if (ptr == NULL) {");
out.println(" return NULL;");
out.println(" }");
out.println("#ifdef MODIFIED_UTF8_STRING");
out.println(" return env->NewStringUTF(ptr);");
out.println("#else");
out.println(" size_t length = strlen(ptr);");
out.println(" jbyteArray bytes = env->NewByteArray(length < INT_MAX ? length : INT_MAX);");
out.println(" env->SetByteArrayRegion(bytes, 0, length < INT_MAX ? length : INT_MAX, (signed char*)ptr);");
out.println(" return (jstring)env->NewObject(JavaCPP_getClass(env, " + jclasses.index(String.class) + "), JavaCPP_stringMID, bytes);");
out.println("#endif");
out.println("}");
out.println();
out.println("static JavaCPP_noinline jstring JavaCPP_createStringFromBytes(JNIEnv* env, const char* ptr) {");
out.println(" if (ptr == NULL) {");
out.println(" return NULL;");
out.println(" }");
out.println(" return JavaCPP_createStringFromBytes(env, ptr, std::char_traits<char>::length(ptr));");
out.println("}");
out.println();
out.println("static JavaCPP_noinline jstring JavaCPP_createStringFromUTF16(JNIEnv* env, const unsigned short* ptr, size_t length) {");
out.println(" if (ptr == NULL) {");
out.println(" return NULL;");
out.println(" }");
out.println(" return env->NewString(ptr, length);");
out.println("}");
out.println();
out.println("static JavaCPP_noinline jstring JavaCPP_createStringFromUTF16(JNIEnv* env, const unsigned short* ptr) {");
out.println(" if (ptr == NULL) {");
out.println(" return NULL;");
out.println(" }");
out.println(" return JavaCPP_createStringFromUTF16(env, ptr, std::char_traits<unsigned short>::length(ptr));");
out.println("}");
out.println();
}
if (convertStrings) {
out.println("static JavaCPP_noinline const char* JavaCPP_getStringBytes(JNIEnv* env, jstring str) {");
Expand All @@ -977,14 +999,31 @@ boolean classes(boolean handleExceptions, boolean defineAdapters, boolean conver
out.println();
out.println("static JavaCPP_noinline void JavaCPP_releaseStringBytes(JNIEnv* env, jstring str, const char* ptr) {");
out.println("#ifdef MODIFIED_UTF8_STRING");
out.println(" if (str != NULL) {");
out.println(" if (str != NULL && ptr != NULL) {");
out.println(" env->ReleaseStringUTFChars(str, ptr);");
out.println(" }");
out.println("#else");
out.println(" delete[] ptr;");
out.println("#endif");
out.println("}");
out.println();
out.println("static JavaCPP_noinline const unsigned short* JavaCPP_getStringUTF16(JNIEnv* env, jstring str) {");
out.println(" if (str == NULL) {");
out.println(" return NULL;");
out.println(" }");
out.println(" const jsize length = env->GetStringLength(str);");
out.println(" unsigned short* ptr = new (std::nothrow) unsigned short[length + 1];");
out.println(" if (ptr != NULL) {");
out.println(" env->GetStringRegion(str, 0, length, ptr);");
out.println(" ptr[length] = 0;");
out.println(" }");
out.println(" return ptr;");
out.println("}");
out.println();
out.println("static JavaCPP_noinline void JavaCPP_releaseStringUTF16(JNIEnv*, const unsigned short* ptr) {");
out.println(" delete[] ptr;");
out.println("}");
out.println();
}
out.println("class JavaCPP_hidden JavaCPP_exception : public std::exception {");
out.println("public:");
Expand Down Expand Up @@ -1012,9 +1051,9 @@ boolean classes(boolean handleExceptions, boolean defineAdapters, boolean conver
out.println(" try {");
out.println(" throw;");
out.println(" } catch (GENERIC_EXCEPTION_CLASS& e) {");
out.println(" str = JavaCPP_createString(env, e.GENERIC_EXCEPTION_TOSTRING);");
out.println(" str = JavaCPP_createStringFromBytes(env, e.GENERIC_EXCEPTION_TOSTRING);");
out.println(" } catch (...) {");
out.println(" str = JavaCPP_createString(env, \"Unknown exception.\");");
out.println(" str = JavaCPP_createStringFromBytes(env, \"Unknown exception.\");");
out.println(" }");
out.println(" jmethodID mid = JavaCPP_getMethodID(env, i, \"<init>\", \"(Ljava/lang/String;)V\");");
out.println(" if (mid == NULL) {");
Expand Down Expand Up @@ -2029,7 +2068,7 @@ void parametersBefore(MethodInformation methodInfo) {
Annotation passBy = by(methodInfo, j);
String cast = cast(methodInfo, j);
String[] typeName = methodInfo.parameterRaw[j] ? new String[] { "" }
: cppTypeName(methodInfo.parameterTypes[j]);
: cppTypeName(methodInfo.parameterTypes[j], methodInfo.parameterAnnotations[j]);
AdapterInformation adapterInfo = methodInfo.parameterRaw[j] ? null
: adapterInformation(false, methodInfo, j);

Expand Down Expand Up @@ -2099,7 +2138,7 @@ void parametersBefore(MethodInformation methodInfo) {
}
} else if (methodInfo.parameterTypes[j] == String.class) {
passesStrings = true;
out.println("JavaCPP_getStringBytes(env, arg" + j + ");");
out.println(getStringData("arg" + j, methodInfo.parameterAnnotations[j]));
if (adapterInfo != null || prevAdapterInfo != null) {
out.println(" jlong size" + j + " = 0;");
out.println(" void* owner" + j + " = (void*)ptr" + j + ";");
Expand Down Expand Up @@ -2268,19 +2307,19 @@ String returnBefore(MethodInformation methodInfo) {
if (FunctionPointer.class.isAssignableFrom(methodInfo.returnType)) {
out.println(" rptr = new (std::nothrow) " + valueTypeName + ";");
if (returnBy instanceof ByPtrPtr) {
String[] cpptypeName = cppTypeName(methodInfo.returnType);
String[] cpptypeName = cppTypeName(methodInfo.returnType, methodInfo.annotations);
returnPrefix = cpptypeName[0] + "* rptrptr" + cpptypeName[1] + " = ";
}
}
} else if (methodInfo.returnType == String.class) {
out.println(" jstring rarg = NULL;");
out.println(" const char* rptr;");
out.println(" " + typeName[0] + " rptr;");
if (returnBy instanceof ByRef) {
returnPrefix = "std::string rstr(";
returnPrefix = "std::basic_string<" + valueTypeName + "> rstr(";
} else if (returnBy instanceof ByPtrPtr) {
returnPrefix = "rptr = NULL; const char** rptrptr = (const char**)";
returnPrefix = "rptr = NULL; " + typeName[0] + "* rptrptr = (" + typeName[0] + "*)";
} else {
returnPrefix += "(const char*)";
returnPrefix += "(" + typeName[0] + ")";
}
} else {
logger.warn("Method \"" + methodInfo.method + "\" has unsupported return type \"" +
Expand Down Expand Up @@ -2691,7 +2730,7 @@ void returnAfter(MethodInformation methodInfo) {
} else if (methodInfo.returnType == String.class) {
passesStrings = true;
out.println(indent + "if (rptr != NULL) {");
out.println(indent + " rarg = JavaCPP_createString(env, rptr);");
out.println(indent + " rarg = " + createString("rptr", (adapterInfo != null ? "radapter" : null), methodInfo.annotations));
out.println(indent + "}");
} else if (methodInfo.returnType.isArray() &&
methodInfo.returnType.getComponentType().isPrimitive()) {
Expand Down Expand Up @@ -2783,7 +2822,7 @@ void parametersAfter(MethodInformation methodInfo) {
", JavaCPP_addressFID, ptr_to_jlong(ptr" + j + "));");
}
} else if (methodInfo.parameterTypes[j] == String.class) {
out.println(" JavaCPP_releaseStringBytes(env, arg" + j + ", ptr" + j + ");");
out.println(" " + releaseStringData("arg" + j, "ptr" + j, methodInfo.parameterAnnotations[j]));
} else if (methodInfo.parameterTypes[j].isArray() &&
methodInfo.parameterTypes[j].getComponentType().isPrimitive()) {
for (int k = 0; adapterInfo != null && k < adapterInfo.argc; k++) {
Expand Down Expand Up @@ -2966,7 +3005,7 @@ void callback(Class<?> cls, Method callbackMethod, String callbackName, int allo
}
String callbackReturnCast = cast(callbackReturnType, callbackAnnotations);
Annotation returnBy = by(callbackAnnotations);
String[] returnTypeName = cppTypeName(callbackReturnType);
String[] returnTypeName = cppTypeName(callbackReturnType, callbackAnnotations);
String returnValueTypeName = valueTypeName(returnTypeName);
AdapterInformation returnAdapterInfo = adapterInformation(false, returnValueTypeName, callbackAnnotations);
boolean throwsExceptions = !noException(cls, callbackMethod);
Expand Down Expand Up @@ -3001,7 +3040,7 @@ void callback(Class<?> cls, Method callbackMethod, String callbackName, int allo
out.println(" }");
}
} else {
String[] typeName = cppTypeName(callbackParameterTypes[j]);
String[] typeName = cppTypeName(callbackParameterTypes[j], callbackParameterAnnotations[j]);
String valueTypeName = valueTypeName(typeName);
AdapterInformation adapterInfo = adapterInformation(false, valueTypeName, callbackParameterAnnotations[j]);

Expand Down Expand Up @@ -3088,7 +3127,12 @@ void callback(Class<?> cls, Method callbackMethod, String callbackName, int allo
out.println(" args[" + j + "].l = obj" + j + ";");
} else if (callbackParameterTypes[j] == String.class) {
passesStrings = true;
out.println(" jstring obj" + j + " = JavaCPP_createString(env, (const char*)" + (adapterInfo != null ? "adapter" : "arg") + j + ");");
if (adapterInfo != null) {
final String adapter = "adapter" + j;
out.println(" jstring obj" + j + " = " + createString("(" + typeName[0] + ") " + adapter, adapter, callbackParameterAnnotations[j]));
} else {
out.println(" jstring obj" + j + " = " + createString("(" + typeName[0] + ") arg" + j, null, callbackParameterAnnotations[j]));
}
out.println(" args[" + j + "].l = obj" + j + ";");
} else if (callbackParameterTypes[j].isArray() &&
callbackParameterTypes[j].getComponentType().isPrimitive()) {
Expand Down Expand Up @@ -3171,7 +3215,7 @@ void callback(Class<?> cls, Method callbackMethod, String callbackName, int allo

for (int j = 0; j < callbackParameterTypes.length; j++) {
if (Pointer.class.isAssignableFrom(callbackParameterTypes[j])) {
String[] typeName = cppTypeName(callbackParameterTypes[j]);
String[] typeName = cppTypeName(callbackParameterTypes[j], callbackParameterAnnotations[j]);
Annotation passBy = by(callbackParameterAnnotations[j]);
String cast = cast(callbackParameterTypes[j], callbackParameterAnnotations[j]);
String valueTypeName = valueTypeName(typeName);
Expand Down Expand Up @@ -3248,7 +3292,7 @@ void callback(Class<?> cls, Method callbackMethod, String callbackName, int allo
}
} else if (callbackReturnType == String.class) {
passesStrings = true;
out.println(" " + returnTypeName[0] + " rptr" + returnTypeName[1] + " = JavaCPP_getStringBytes(env, rarg);");
out.println(" " + returnTypeName[0] + " rptr" + returnTypeName[1] + " = " + getStringData("rarg", callbackAnnotations));
if (returnAdapterInfo != null) {
out.println(" jlong rsize = 0;");
out.println(" void* rowner = (void*)rptr;");
Expand Down Expand Up @@ -3822,6 +3866,34 @@ String enumValueType(Class<?> type) {
}
}

static boolean asUtf16(Annotation[] annotations) {
if (annotations == null) {
return false;
}
for (Annotation annotation : annotations) {
if (annotation instanceof AsUtf16) {
return true;
}
}
return false;
}

static String createString(String ptr, String adapter, Annotation[] annotations) {
return (asUtf16(annotations) ? "JavaCPP_createStringFromUTF16(env, "
: "JavaCPP_createStringFromBytes(env, ")
+ ptr + (adapter != null ? ", " + adapter + ".size);" : ");");
}

static String getStringData(String str, Annotation[] annotations) {
return (asUtf16(annotations) ? "JavaCPP_getStringUTF16(env, "
: "JavaCPP_getStringBytes(env, ") + str + ");";
}

static String releaseStringData(String str, String ptr, Annotation[] annotations) {
return (asUtf16(annotations) ? "JavaCPP_releaseStringUTF16(env, "
: "JavaCPP_releaseStringBytes(env, " + str + ", ") + ptr + ");";
}

static String constValueTypeName(String ... typeName) {
String type = typeName[0];
if (type.endsWith("*") || type.endsWith("&")) {
Expand Down Expand Up @@ -3930,7 +4002,7 @@ String[] cppCastTypeName(Class<?> type, Annotation ... annotations) {
// prioritize @Cast
continue;
}
typeName = cppTypeName(type);
typeName = cppTypeName(type, annotations);
if (typeName[0].contains("(*")) {
// function pointer
if (b.length > 0 && b[0] && !typeName[0].endsWith(" const")) {
Expand Down Expand Up @@ -3958,12 +4030,16 @@ String[] cppCastTypeName(Class<?> type, Annotation ... annotations) {
logger.warn("Without \"Adapter\", \"Cast\" and \"Const\" annotations are mutually exclusive.");
}
if (typeName == null) {
typeName = cppTypeName(type);
typeName = cppTypeName(type, annotations);
}
return typeName;
}

String[] cppTypeName(Class<?> type) {
return cppTypeName(type, null);
}

String[] cppTypeName(Class<?> type, Annotation[] annotations) {
String prefix = "", suffix = "";
if (type == Buffer.class || type == Pointer.class) {
prefix = "void*";
Expand All @@ -3986,7 +4062,11 @@ String[] cppTypeName(Class<?> type) {
} else if (type == PointerPointer.class) {
prefix = "void**";
} else if (type == String.class) {
prefix = "const char*";
if (asUtf16(annotations)) {
prefix = "const unsigned short*";
} else {
prefix = "const char*";
}
} else if (type == byte.class) {
prefix = "signed char";
} else if (type == long.class) {
Expand Down
Loading