mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1178850 - Generate naive method bindings in annotation processor; r=snorp
This commit is contained in:
parent
5afbfdc70f
commit
af3069af19
@ -15,14 +15,15 @@ import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class AnnotationProcessor {
|
||||
public static final String OUTFILE = "GeneratedJNIWrappers.cpp";
|
||||
public static final String HEADERFILE = "GeneratedJNIWrappers.h";
|
||||
public static final String SOURCE_FILE = "GeneratedJNIWrappers.cpp";
|
||||
public static final String HEADER_FILE = "GeneratedJNIWrappers.h";
|
||||
public static final String NATIVES_FILE = "GeneratedJNINatives.h";
|
||||
|
||||
public static final String GENERATED_COMMENT =
|
||||
"// GENERATED CODE\n" +
|
||||
"// Generated by the Java program at /build/annotationProcessors at compile time\n" +
|
||||
"// from annotations on Java methods. To update, change the annotations on the\n" +
|
||||
"// corresponding Javamethods and rerun the build. Manually updating this file\n" +
|
||||
"// corresponding Java methods and rerun the build. Manually updating this file\n" +
|
||||
"// will cause your build to fail.\n" +
|
||||
"\n";
|
||||
|
||||
@ -47,8 +48,8 @@ public class AnnotationProcessor {
|
||||
|
||||
StringBuilder headerFile = new StringBuilder(GENERATED_COMMENT);
|
||||
headerFile.append(
|
||||
"#ifndef GeneratedJNIWrappers_h__\n" +
|
||||
"#define GeneratedJNIWrappers_h__\n" +
|
||||
"#ifndef " + getHeaderGuardName(HEADER_FILE) + "\n" +
|
||||
"#define " + getHeaderGuardName(HEADER_FILE) + "\n" +
|
||||
"\n" +
|
||||
"#include \"mozilla/jni/Refs.h\"\n" +
|
||||
"\n" +
|
||||
@ -65,6 +66,18 @@ public class AnnotationProcessor {
|
||||
"namespace widget {\n" +
|
||||
"\n");
|
||||
|
||||
StringBuilder nativesFile = new StringBuilder(GENERATED_COMMENT);
|
||||
nativesFile.append(
|
||||
"#ifndef " + getHeaderGuardName(NATIVES_FILE) + "\n" +
|
||||
"#define " + getHeaderGuardName(NATIVES_FILE) + "\n" +
|
||||
"\n" +
|
||||
"#include \"GeneratedJNIWrappers.h\"\n" +
|
||||
"#include \"mozilla/jni/Natives.h\"\n" +
|
||||
"\n" +
|
||||
"namespace mozilla {\n" +
|
||||
"namespace widget {\n" +
|
||||
"\n");
|
||||
|
||||
while (jarClassIterator.hasNext()) {
|
||||
ClassWithOptions aClassTuple = jarClassIterator.next();
|
||||
|
||||
@ -85,6 +98,9 @@ public class AnnotationProcessor {
|
||||
case METHOD:
|
||||
generatorInstance.generateMethod(aElementTuple);
|
||||
break;
|
||||
case NATIVE:
|
||||
generatorInstance.generateNative(aElementTuple);
|
||||
break;
|
||||
case FIELD:
|
||||
generatorInstance.generateField(aElementTuple);
|
||||
break;
|
||||
@ -96,6 +112,7 @@ public class AnnotationProcessor {
|
||||
|
||||
headerFile.append(generatorInstance.getHeaderFileContents());
|
||||
implementationFile.append(generatorInstance.getWrapperFileContents());
|
||||
nativesFile.append(generatorInstance.getNativesFileContents());
|
||||
}
|
||||
|
||||
implementationFile.append(
|
||||
@ -107,38 +124,33 @@ public class AnnotationProcessor {
|
||||
"\n" +
|
||||
"} /* widget */\n" +
|
||||
"} /* mozilla */\n" +
|
||||
"#endif // GeneratedJNIWrappers_h__\n");
|
||||
"#endif // " + getHeaderGuardName(HEADER_FILE) + "\n");
|
||||
|
||||
writeOutputFiles(headerFile, implementationFile);
|
||||
nativesFile.append(
|
||||
"\n" +
|
||||
"} /* widget */\n" +
|
||||
"} /* mozilla */\n" +
|
||||
"#endif // " + getHeaderGuardName(NATIVES_FILE) + "\n");
|
||||
|
||||
writeOutputFile(SOURCE_FILE, implementationFile);
|
||||
writeOutputFile(HEADER_FILE, headerFile);
|
||||
writeOutputFile(NATIVES_FILE, nativesFile);
|
||||
long e = System.currentTimeMillis();
|
||||
System.out.println("Annotation processing complete in " + (e - s) + "ms");
|
||||
}
|
||||
|
||||
private static void writeOutputFiles(StringBuilder aHeaderFile, StringBuilder aImplementationFile) {
|
||||
FileOutputStream headerStream = null;
|
||||
try {
|
||||
headerStream = new FileOutputStream(OUTFILE);
|
||||
headerStream.write(aImplementationFile.toString().getBytes());
|
||||
} catch (IOException e) {
|
||||
System.err.println("Unable to write " + OUTFILE + ". Perhaps a permissions issue?");
|
||||
e.printStackTrace(System.err);
|
||||
} finally {
|
||||
if (headerStream != null) {
|
||||
try {
|
||||
headerStream.close();
|
||||
} catch (IOException e) {
|
||||
System.err.println("Unable to close headerStream due to "+e);
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}
|
||||
private static String getHeaderGuardName(final String name) {
|
||||
return name.replaceAll("\\W", "_");
|
||||
}
|
||||
|
||||
private static void writeOutputFile(final String name,
|
||||
final StringBuilder content) {
|
||||
FileOutputStream outStream = null;
|
||||
try {
|
||||
outStream = new FileOutputStream(HEADERFILE);
|
||||
outStream.write(aHeaderFile.toString().getBytes());
|
||||
outStream = new FileOutputStream(name);
|
||||
outStream.write(content.toString().getBytes());
|
||||
} catch (IOException e) {
|
||||
System.err.println("Unable to write " + HEADERFILE + ". Perhaps a permissions issue?");
|
||||
System.err.println("Unable to write " + name + ". Perhaps a permissions issue?");
|
||||
e.printStackTrace(System.err);
|
||||
} finally {
|
||||
if (outStream != null) {
|
||||
|
@ -22,6 +22,8 @@ public class CodeGenerator {
|
||||
// Buffers holding the strings to ultimately be written to the output files.
|
||||
private final StringBuilder cpp = new StringBuilder();
|
||||
private final StringBuilder header = new StringBuilder();
|
||||
private final StringBuilder natives = new StringBuilder();
|
||||
private final StringBuilder nativesInits = new StringBuilder();
|
||||
|
||||
private final Class<?> cls;
|
||||
private final String clsName;
|
||||
@ -33,8 +35,8 @@ public class CodeGenerator {
|
||||
this.clsName = annotatedClass.generatedName;
|
||||
|
||||
header.append(
|
||||
"class " + clsName + " : public mozilla::jni::Class<" + clsName + "> {\n" +
|
||||
"\n" +
|
||||
"class " + clsName + " : public mozilla::jni::Class<" + clsName + ">\n" +
|
||||
"{\n" +
|
||||
"public:\n" +
|
||||
" typedef mozilla::jni::Ref<" + clsName + "> Ref;\n" +
|
||||
" typedef mozilla::jni::LocalRef<" + clsName + "> LocalRef;\n" +
|
||||
@ -51,6 +53,12 @@ public class CodeGenerator {
|
||||
cpp.append(
|
||||
"constexpr char " + clsName + "::name[];\n" +
|
||||
"\n");
|
||||
|
||||
natives.append(
|
||||
"template<class Impl>\n" +
|
||||
"class " + clsName + "::Natives : " +
|
||||
"public mozilla::jni::NativeImpl<" + clsName + ", Impl>\n" +
|
||||
"{\n");
|
||||
}
|
||||
|
||||
private String getTraitsName(String uniqueName, boolean includeScope) {
|
||||
@ -72,20 +80,30 @@ public class CodeGenerator {
|
||||
}
|
||||
|
||||
private void generateMember(AnnotationInfo info, Member member,
|
||||
String uniqueName, Class<?> type) {
|
||||
String uniqueName, Class<?> type, Class<?>[] argTypes) {
|
||||
final StringBuilder args = new StringBuilder();
|
||||
for (Class<?> argType : argTypes) {
|
||||
args.append("\n " + getNativeParameterType(argType, info) + ",");
|
||||
}
|
||||
if (args.length() > 0) {
|
||||
args.setLength(args.length() - 1);
|
||||
}
|
||||
|
||||
header.append(
|
||||
"public:\n" +
|
||||
" struct " + getTraitsName(uniqueName, /* includeScope */ false) + " {\n" +
|
||||
" typedef " + clsName + " Owner;\n" +
|
||||
" typedef " + getNativeReturnType(type, info) + " ReturnType;\n" +
|
||||
" typedef " + getNativeParameterType(type, info) + " SetterType;\n" +
|
||||
" typedef mozilla::jni::Args<" + args + "> Args;\n" +
|
||||
" static constexpr char name[] = \"" +
|
||||
Utils.getMemberName(member) + "\";\n" +
|
||||
" static constexpr char signature[] =\n" +
|
||||
" \"" + Utils.getSignature(member) + "\";\n" +
|
||||
" static const bool isStatic = " + Utils.isStatic(member) + ";\n" +
|
||||
" static const bool isMultithreaded = " + info.isMultithreaded + ";\n" +
|
||||
" static const mozilla::jni::ExceptionMode exceptionMode = " + (
|
||||
" static const mozilla::jni::ExceptionMode exceptionMode =\n" +
|
||||
" " + (
|
||||
info.catchException ? "mozilla::jni::ExceptionMode::NSRESULT" :
|
||||
info.noThrow ? "mozilla::jni::ExceptionMode::IGNORE" :
|
||||
"mozilla::jni::ExceptionMode::ABORT") + ";\n" +
|
||||
@ -248,15 +266,15 @@ public class CodeGenerator {
|
||||
final Method method = annotatedMethod.getMethod();
|
||||
final AnnotationInfo info = annotatedMethod.mAnnotationInfo;
|
||||
final String uniqueName = getUniqueMethodName(info.wrapperName);
|
||||
final Class<?>[] argTypes = method.getParameterTypes();
|
||||
final Class<?> returnType = method.getReturnType();
|
||||
|
||||
if (method.isSynthetic()) {
|
||||
return;
|
||||
}
|
||||
|
||||
generateMember(info, method, uniqueName, returnType);
|
||||
generateMember(info, method, uniqueName, returnType, argTypes);
|
||||
|
||||
final Class<?>[] argTypes = method.getParameterTypes();
|
||||
final boolean isStatic = Utils.isStatic(method);
|
||||
|
||||
header.append(
|
||||
@ -272,6 +290,35 @@ public class CodeGenerator {
|
||||
"\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the appropriate generated code to the buffers for the native method provided.
|
||||
*
|
||||
* @param annotatedMethod The Java native method, plus annotation data.
|
||||
*/
|
||||
public void generateNative(AnnotatableEntity annotatedMethod) {
|
||||
// Unpack the tuple and extract some useful fields from the Method..
|
||||
final Method method = annotatedMethod.getMethod();
|
||||
final AnnotationInfo info = annotatedMethod.mAnnotationInfo;
|
||||
final String uniqueName = getUniqueMethodName(info.wrapperName);
|
||||
final Class<?>[] argTypes = method.getParameterTypes();
|
||||
final Class<?> returnType = method.getReturnType();
|
||||
|
||||
generateMember(info, method, uniqueName, returnType, argTypes);
|
||||
|
||||
final String traits = getTraitsName(uniqueName, /* includeScope */ true);
|
||||
|
||||
if (nativesInits.length() > 0) {
|
||||
nativesInits.append(',');
|
||||
}
|
||||
|
||||
nativesInits.append(
|
||||
"\n" +
|
||||
"\n" +
|
||||
" mozilla::jni::MakeNativeMethod<" + traits + ">(\n" +
|
||||
" mozilla::jni::NativeStub<" + traits + ", Impl>\n" +
|
||||
" ::template Wrap<&Impl::" + info.wrapperName + ">)");
|
||||
}
|
||||
|
||||
private String getLiteral(Object val, AnnotationInfo info) {
|
||||
final Class<?> type = val.getClass();
|
||||
|
||||
@ -348,7 +395,7 @@ public class CodeGenerator {
|
||||
// Fall back to using accessors if we encounter an exception.
|
||||
}
|
||||
|
||||
generateMember(info, field, uniqueName, type);
|
||||
generateMember(info, field, uniqueName, type, EMPTY_CLASS_ARRAY);
|
||||
|
||||
final Class<?>[] getterArgs = EMPTY_CLASS_ARRAY;
|
||||
|
||||
@ -389,15 +436,14 @@ public class CodeGenerator {
|
||||
final AnnotationInfo info = annotatedConstructor.mAnnotationInfo;
|
||||
final String wrapperName = "New";
|
||||
final String uniqueName = getUniqueMethodName(wrapperName);
|
||||
final Class<?>[] argTypes = method.getParameterTypes();
|
||||
final Class<?> returnType = cls;
|
||||
|
||||
if (method.isSynthetic()) {
|
||||
return;
|
||||
}
|
||||
|
||||
generateMember(info, method, uniqueName, returnType);
|
||||
|
||||
final Class<?>[] argTypes = method.getParameterTypes();
|
||||
generateMember(info, method, uniqueName, returnType, argTypes);
|
||||
|
||||
header.append(
|
||||
" " + generateDeclaration(wrapperName, argTypes,
|
||||
@ -454,9 +500,34 @@ public class CodeGenerator {
|
||||
* @return The bytes to be written to the header file.
|
||||
*/
|
||||
public String getHeaderFileContents() {
|
||||
if (nativesInits.length() > 0) {
|
||||
header.append(
|
||||
"public:\n" +
|
||||
" template<class Impl> class Natives;\n");
|
||||
}
|
||||
header.append(
|
||||
"};\n" +
|
||||
"\n");
|
||||
return header.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the finalised bytes to go into the generated natives header file.
|
||||
*
|
||||
* @return The bytes to be written to the header file.
|
||||
*/
|
||||
public String getNativesFileContents() {
|
||||
if (nativesInits.length() == 0) {
|
||||
return "";
|
||||
}
|
||||
natives.append(
|
||||
"public:\n" +
|
||||
" static constexpr JNINativeMethod methods[] = {" + nativesInits + '\n' +
|
||||
" };\n" +
|
||||
"};\n" +
|
||||
"\n" +
|
||||
"template<class Impl>\n" +
|
||||
"constexpr JNINativeMethod " + clsName + "::Natives<Impl>::methods[];\n");
|
||||
return natives.toString();
|
||||
}
|
||||
}
|
||||
|
@ -10,13 +10,14 @@ import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
/**
|
||||
* Union type to hold either a method, field, or ctor. Allows us to iterate "The generatable stuff", despite
|
||||
* the fact that such things can be of either flavour.
|
||||
*/
|
||||
public class AnnotatableEntity {
|
||||
public enum ENTITY_TYPE {METHOD, FIELD, CONSTRUCTOR}
|
||||
public enum ENTITY_TYPE {METHOD, NATIVE, FIELD, CONSTRUCTOR}
|
||||
|
||||
private final Member mMember;
|
||||
public final ENTITY_TYPE mEntityType;
|
||||
@ -28,7 +29,11 @@ public class AnnotatableEntity {
|
||||
mAnnotationInfo = aAnnotationInfo;
|
||||
|
||||
if (aObject instanceof Method) {
|
||||
mEntityType = ENTITY_TYPE.METHOD;
|
||||
if (Modifier.isNative(aObject.getModifiers())) {
|
||||
mEntityType = ENTITY_TYPE.NATIVE;
|
||||
} else {
|
||||
mEntityType = ENTITY_TYPE.METHOD;
|
||||
}
|
||||
} else if (aObject instanceof Field) {
|
||||
mEntityType = ENTITY_TYPE.FIELD;
|
||||
} else {
|
||||
@ -37,7 +42,7 @@ public class AnnotatableEntity {
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
if (mEntityType != ENTITY_TYPE.METHOD) {
|
||||
if (mEntityType != ENTITY_TYPE.METHOD && mEntityType != ENTITY_TYPE.NATIVE) {
|
||||
throw new UnsupportedOperationException("Attempt to cast to unsupported member type.");
|
||||
}
|
||||
return (Method) mMember;
|
||||
|
Loading…
Reference in New Issue
Block a user