1087 lines
40 KiB
Java
1087 lines
40 KiB
Java
|
import java.io.PrintStream;
|
||
|
import java.io.FileNotFoundException;
|
||
|
import java.io.FileOutputStream;
|
||
|
import java.util.ArrayList;
|
||
|
import java.util.HashSet;
|
||
|
import java.util.Iterator;
|
||
|
import java.util.List;
|
||
|
|
||
|
/**
|
||
|
* Emits a Java interface and Java & C implementation for a C function.
|
||
|
*
|
||
|
* <p> The Java interface will have Buffer and array variants for functions that
|
||
|
* have a typed pointer argument. The array variant will convert a single "<type> *data"
|
||
|
* argument to a pair of arguments "<type>[] data, int offset".
|
||
|
*/
|
||
|
public class JniCodeEmitter implements CodeEmitter {
|
||
|
|
||
|
// If true, use C++ style for calling through a JNIEnv *:
|
||
|
// env->Func(...)
|
||
|
// If false, use C style:
|
||
|
// (*env)->Func(env, ...)
|
||
|
static final boolean mUseCPlusPlus = true;
|
||
|
|
||
|
boolean mUseContextPointer = true;
|
||
|
|
||
|
String mClassPathName;
|
||
|
|
||
|
ParameterChecker mChecker;
|
||
|
PrintStream mJava10InterfaceStream;
|
||
|
PrintStream mJava10ExtInterfaceStream;
|
||
|
PrintStream mJava11InterfaceStream;
|
||
|
PrintStream mJava11ExtInterfaceStream;
|
||
|
PrintStream mJava11ExtPackInterfaceStream;
|
||
|
PrintStream mJavaImplStream;
|
||
|
PrintStream mCStream;
|
||
|
|
||
|
PrintStream mJavaInterfaceStream;
|
||
|
|
||
|
List<String> nativeRegistrations = new ArrayList<String>();
|
||
|
|
||
|
boolean needsExit;
|
||
|
|
||
|
static String indent = " ";
|
||
|
|
||
|
HashSet<String> mFunctionsEmitted = new HashSet<String>();
|
||
|
|
||
|
/**
|
||
|
* @param java10InterfaceStream the PrintStream to which to emit the Java interface for GL 1.0 functions
|
||
|
* @param java10ExtInterfaceStream the PrintStream to which to emit the Java interface for GL 1.0 extension functions
|
||
|
* @param java11InterfaceStream the PrintStream to which to emit the Java interface for GL 1.1 functions
|
||
|
* @param java11ExtInterfaceStream the PrintStream to which to emit the Java interface for GL 1.1 Extension functions
|
||
|
* @param java11ExtPackInterfaceStream the PrintStream to which to emit the Java interface for GL 1.1 Extension Pack functions
|
||
|
* @param javaImplStream the PrintStream to which to emit the Java implementation
|
||
|
* @param cStream the PrintStream to which to emit the C implementation
|
||
|
*/
|
||
|
public JniCodeEmitter(String classPathName,
|
||
|
ParameterChecker checker,
|
||
|
PrintStream java10InterfaceStream,
|
||
|
PrintStream java10ExtInterfaceStream,
|
||
|
PrintStream java11InterfaceStream,
|
||
|
PrintStream java11ExtInterfaceStream,
|
||
|
PrintStream java11ExtPackInterfaceStream,
|
||
|
PrintStream javaImplStream,
|
||
|
PrintStream cStream,
|
||
|
boolean useContextPointer) {
|
||
|
mClassPathName = classPathName;
|
||
|
mChecker = checker;
|
||
|
mJava10InterfaceStream = java10InterfaceStream;
|
||
|
mJava10ExtInterfaceStream = java10ExtInterfaceStream;
|
||
|
mJava11InterfaceStream = java11InterfaceStream;
|
||
|
mJava11ExtInterfaceStream = java11ExtInterfaceStream;
|
||
|
mJava11ExtPackInterfaceStream = java11ExtPackInterfaceStream;
|
||
|
mJavaImplStream = javaImplStream;
|
||
|
mCStream = cStream;
|
||
|
mUseContextPointer = useContextPointer;
|
||
|
}
|
||
|
|
||
|
public void setVersion(int version, boolean ext, boolean pack) {
|
||
|
if (version == 0) {
|
||
|
mJavaInterfaceStream = ext ? mJava10ExtInterfaceStream :
|
||
|
mJava10InterfaceStream;
|
||
|
} else if (version == 1) {
|
||
|
mJavaInterfaceStream = ext ?
|
||
|
(pack ? mJava11ExtPackInterfaceStream :
|
||
|
mJava11ExtInterfaceStream) :
|
||
|
mJava11InterfaceStream;
|
||
|
} else {
|
||
|
throw new RuntimeException("Bad version: " + version);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void emitCode(CFunc cfunc, String original) {
|
||
|
JFunc jfunc;
|
||
|
String signature;
|
||
|
boolean duplicate;
|
||
|
|
||
|
if (cfunc.hasTypedPointerArg()) {
|
||
|
jfunc = JFunc.convert(cfunc, true);
|
||
|
|
||
|
// Don't emit duplicate functions
|
||
|
// These may appear because they are defined in multiple
|
||
|
// Java interfaces (e.g., GL11/GL11ExtensionPack)
|
||
|
signature = jfunc.toString();
|
||
|
duplicate = false;
|
||
|
if (mFunctionsEmitted.contains(signature)) {
|
||
|
duplicate = true;
|
||
|
} else {
|
||
|
mFunctionsEmitted.add(signature);
|
||
|
}
|
||
|
|
||
|
if (!duplicate) {
|
||
|
emitNativeDeclaration(jfunc, mJavaImplStream);
|
||
|
emitJavaCode(jfunc, mJavaImplStream);
|
||
|
}
|
||
|
emitJavaInterfaceCode(jfunc, mJavaInterfaceStream);
|
||
|
if (!duplicate) {
|
||
|
emitJniCode(jfunc, mCStream);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
jfunc = JFunc.convert(cfunc, false);
|
||
|
|
||
|
signature = jfunc.toString();
|
||
|
duplicate = false;
|
||
|
if (mFunctionsEmitted.contains(signature)) {
|
||
|
duplicate = true;
|
||
|
} else {
|
||
|
mFunctionsEmitted.add(signature);
|
||
|
}
|
||
|
|
||
|
if (!duplicate) {
|
||
|
emitNativeDeclaration(jfunc, mJavaImplStream);
|
||
|
}
|
||
|
emitJavaInterfaceCode(jfunc, mJavaInterfaceStream);
|
||
|
if (!duplicate) {
|
||
|
emitJavaCode(jfunc, mJavaImplStream);
|
||
|
emitJniCode(jfunc, mCStream);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void emitNativeDeclaration(JFunc jfunc, PrintStream out) {
|
||
|
out.println(" // C function " + jfunc.getCFunc().getOriginal());
|
||
|
out.println();
|
||
|
|
||
|
emitFunction(jfunc, out, true, false);
|
||
|
}
|
||
|
|
||
|
public void emitJavaInterfaceCode(JFunc jfunc, PrintStream out) {
|
||
|
emitFunction(jfunc, out, false, true);
|
||
|
}
|
||
|
|
||
|
public void emitJavaCode(JFunc jfunc, PrintStream out) {
|
||
|
emitFunction(jfunc, out, false, false);
|
||
|
}
|
||
|
|
||
|
void emitFunctionCall(JFunc jfunc, PrintStream out, String iii, boolean grabArray ) {
|
||
|
boolean isVoid = jfunc.getType().isVoid();
|
||
|
boolean isPointerFunc = jfunc.getName().endsWith("Pointer") &&
|
||
|
jfunc.getCFunc().hasPointerArg();
|
||
|
|
||
|
if (!isVoid) {
|
||
|
out.println(iii +
|
||
|
jfunc.getType() + " _returnValue;");
|
||
|
}
|
||
|
out.println(iii +
|
||
|
(isVoid ? "" : "_returnValue = ") +
|
||
|
jfunc.getName() +
|
||
|
(isPointerFunc ? "Bounds" : "" ) +
|
||
|
"(");
|
||
|
|
||
|
int numArgs = jfunc.getNumArgs();
|
||
|
for (int i = 0; i < numArgs; i++) {
|
||
|
String argName = jfunc.getArgName(i);
|
||
|
JType argType = jfunc.getArgType(i);
|
||
|
|
||
|
if (grabArray && argType.isTypedBuffer()) {
|
||
|
String typeName = argType.getBaseType();
|
||
|
typeName = typeName.substring(9, typeName.length() - 6);
|
||
|
out.println(iii + indent + "get" + typeName + "Array(" + argName + "),");
|
||
|
out.print(iii + indent + "getOffset(" + argName + ")");
|
||
|
} else {
|
||
|
out.print(iii + indent + argName);
|
||
|
}
|
||
|
if (i == numArgs - 1) {
|
||
|
if (isPointerFunc) {
|
||
|
out.println(",");
|
||
|
out.println(iii + indent + argName + ".remaining()");
|
||
|
} else {
|
||
|
out.println();
|
||
|
}
|
||
|
} else {
|
||
|
out.println(",");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
out.println(iii + ");");
|
||
|
}
|
||
|
|
||
|
void printIfcheckPostamble(PrintStream out, boolean isBuffer,
|
||
|
boolean emitExceptionCheck, String iii) {
|
||
|
printIfcheckPostamble(out, isBuffer, emitExceptionCheck,
|
||
|
"offset", "_remaining", iii);
|
||
|
}
|
||
|
|
||
|
void printIfcheckPostamble(PrintStream out, boolean isBuffer,
|
||
|
boolean emitExceptionCheck,
|
||
|
String offset, String remaining, String iii) {
|
||
|
out.println(iii + " default:");
|
||
|
out.println(iii + " _needed = 0;");
|
||
|
out.println(iii + " break;");
|
||
|
out.println(iii + "}");
|
||
|
|
||
|
out.println(iii + "if (" + remaining + " < _needed) {");
|
||
|
if (emitExceptionCheck) {
|
||
|
out.println(iii + indent + "_exception = 1;");
|
||
|
}
|
||
|
out.println(iii + indent +
|
||
|
(mUseCPlusPlus ? "_env" : "(*_env)") +
|
||
|
"->ThrowNew(" +
|
||
|
(mUseCPlusPlus ? "" : "_env, ") +
|
||
|
"IAEClass, " +
|
||
|
"\"" +
|
||
|
(isBuffer ?
|
||
|
"remaining()" : "length - " + offset) +
|
||
|
" < needed\");");
|
||
|
out.println(iii + indent + "goto exit;");
|
||
|
needsExit = true;
|
||
|
out.println(iii + "}");
|
||
|
}
|
||
|
|
||
|
boolean isNullAllowed(CFunc cfunc) {
|
||
|
String[] checks = mChecker.getChecks(cfunc.getName());
|
||
|
int index = 1;
|
||
|
if (checks != null) {
|
||
|
while (index < checks.length) {
|
||
|
if (checks[index].equals("return")) {
|
||
|
index += 2;
|
||
|
} else if (checks[index].startsWith("check")) {
|
||
|
index += 3;
|
||
|
} else if (checks[index].equals("ifcheck")) {
|
||
|
index += 5;
|
||
|
} else if (checks[index].equals("unsupported")) {
|
||
|
index += 1;
|
||
|
} else if (checks[index].equals("nullAllowed")) {
|
||
|
return true;
|
||
|
} else {
|
||
|
System.out.println("Error: unknown keyword \"" +
|
||
|
checks[index] + "\"");
|
||
|
System.exit(0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
String getErrorReturnValue(CFunc cfunc) {
|
||
|
CType returnType = cfunc.getType();
|
||
|
boolean isVoid = returnType.isVoid();
|
||
|
if (isVoid) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
String[] checks = mChecker.getChecks(cfunc.getName());
|
||
|
|
||
|
int index = 1;
|
||
|
if (checks != null) {
|
||
|
while (index < checks.length) {
|
||
|
if (checks[index].equals("return")) {
|
||
|
return checks[index + 1];
|
||
|
} else if (checks[index].startsWith("check")) {
|
||
|
index += 3;
|
||
|
} else if (checks[index].equals("ifcheck")) {
|
||
|
index += 5;
|
||
|
} else if (checks[index].equals("unsupported")) {
|
||
|
index += 1;
|
||
|
} else if (checks[index].equals("nullAllowed")) {
|
||
|
index += 1;
|
||
|
} else {
|
||
|
System.out.println("Error: unknown keyword \"" +
|
||
|
checks[index] + "\"");
|
||
|
System.exit(0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
boolean isUnsupportedFunc(CFunc cfunc) {
|
||
|
String[] checks = mChecker.getChecks(cfunc.getName());
|
||
|
int index = 1;
|
||
|
if (checks != null) {
|
||
|
while (index < checks.length) {
|
||
|
if (checks[index].equals("unsupported")) {
|
||
|
return true;
|
||
|
} else if (checks[index].equals("return")) {
|
||
|
index += 2;
|
||
|
} else if (checks[index].startsWith("check")) {
|
||
|
index += 3;
|
||
|
} else if (checks[index].equals("ifcheck")) {
|
||
|
index += 5;
|
||
|
} else if (checks[index].equals("nullAllowed")) {
|
||
|
index += 1;
|
||
|
} else {
|
||
|
System.out.println("Error: unknown keyword \"" +
|
||
|
checks[index] + "\"");
|
||
|
System.exit(0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void emitNativeBoundsChecks(CFunc cfunc, String cname, PrintStream out,
|
||
|
boolean isBuffer, boolean emitExceptionCheck,
|
||
|
String offset, String remaining, String iii) {
|
||
|
CType returnType = cfunc.getType();
|
||
|
boolean isVoid = returnType.isVoid();
|
||
|
|
||
|
String[] checks = mChecker.getChecks(cfunc.getName());
|
||
|
String checkVar;
|
||
|
String retval = getErrorReturnValue(cfunc);
|
||
|
|
||
|
boolean lastWasIfcheck = false;
|
||
|
|
||
|
int index = 1;
|
||
|
if (checks != null) {
|
||
|
boolean remainingDeclared = false;
|
||
|
boolean nullCheckDeclared = false;
|
||
|
boolean offsetChecked = false;
|
||
|
while (index < checks.length) {
|
||
|
if (checks[index].startsWith("check")) {
|
||
|
if (lastWasIfcheck) {
|
||
|
printIfcheckPostamble(out, isBuffer, emitExceptionCheck,
|
||
|
offset, remaining, iii);
|
||
|
}
|
||
|
lastWasIfcheck = false;
|
||
|
if (cname != null && !cname.equals(checks[index + 1])) {
|
||
|
index += 3;
|
||
|
continue;
|
||
|
}
|
||
|
out.println(iii + "if (" + remaining + " < " +
|
||
|
checks[index + 2] +
|
||
|
") {");
|
||
|
if (emitExceptionCheck) {
|
||
|
out.println(iii + indent + "_exception = 1;");
|
||
|
}
|
||
|
String exceptionClassName = "IAEClass";
|
||
|
// If the "check" keyword was of the form
|
||
|
// "check_<class name>", use the class name in the
|
||
|
// exception to be thrown
|
||
|
int underscore = checks[index].indexOf('_');
|
||
|
if (underscore >= 0) {
|
||
|
exceptionClassName = checks[index].substring(underscore + 1) + "Class";
|
||
|
}
|
||
|
out.println(iii + indent +
|
||
|
(mUseCPlusPlus ? "_env" : "(*_env)") +
|
||
|
"->ThrowNew(" +
|
||
|
(mUseCPlusPlus ? "" : "_env, ") +
|
||
|
exceptionClassName + ", " +
|
||
|
"\"" +
|
||
|
(isBuffer ?
|
||
|
"remaining()" : "length - " + offset) +
|
||
|
" < " + checks[index + 2] +
|
||
|
"\");");
|
||
|
|
||
|
out.println(iii + indent + "goto exit;");
|
||
|
needsExit = true;
|
||
|
out.println(iii + "}");
|
||
|
|
||
|
index += 3;
|
||
|
} else if (checks[index].equals("ifcheck")) {
|
||
|
String[] matches = checks[index + 4].split(",");
|
||
|
|
||
|
if (!lastWasIfcheck) {
|
||
|
out.println(iii + "int _needed;");
|
||
|
out.println(iii +
|
||
|
"switch (" +
|
||
|
checks[index + 3] +
|
||
|
") {");
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < matches.length; i++) {
|
||
|
out.println("#if defined(" + matches[i] + ")");
|
||
|
out.println(iii +
|
||
|
" case " +
|
||
|
matches[i] +
|
||
|
":");
|
||
|
out.println("#endif // defined(" + matches[i] + ")");
|
||
|
}
|
||
|
out.println(iii +
|
||
|
" _needed = " +
|
||
|
checks[index + 2] +
|
||
|
";");
|
||
|
out.println(iii +
|
||
|
" break;");
|
||
|
|
||
|
lastWasIfcheck = true;
|
||
|
index += 5;
|
||
|
} else if (checks[index].equals("return")) {
|
||
|
// ignore
|
||
|
index += 2;
|
||
|
} else if (checks[index].equals("unsupported")) {
|
||
|
// ignore
|
||
|
index += 1;
|
||
|
} else if (checks[index].equals("nullAllowed")) {
|
||
|
// ignore
|
||
|
index += 1;
|
||
|
} else {
|
||
|
System.out.println("Error: unknown keyword \"" +
|
||
|
checks[index] + "\"");
|
||
|
System.exit(0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (lastWasIfcheck) {
|
||
|
printIfcheckPostamble(out, isBuffer, emitExceptionCheck, iii);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
boolean hasNonConstArg(JFunc jfunc, CFunc cfunc,
|
||
|
List<Integer> nonPrimitiveArgs) {
|
||
|
if (nonPrimitiveArgs.size() > 0) {
|
||
|
for (int i = nonPrimitiveArgs.size() - 1; i >= 0; i--) {
|
||
|
int idx = nonPrimitiveArgs.get(i).intValue();
|
||
|
int cIndex = jfunc.getArgCIndex(idx);
|
||
|
if (jfunc.getArgType(idx).isArray()) {
|
||
|
if (!cfunc.getArgType(cIndex).isConst()) {
|
||
|
return true;
|
||
|
}
|
||
|
} else if (jfunc.getArgType(idx).isBuffer()) {
|
||
|
if (!cfunc.getArgType(cIndex).isConst()) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Emit a function in several variants:
|
||
|
*
|
||
|
* if nativeDecl: public native <returntype> func(args);
|
||
|
*
|
||
|
* if !nativeDecl:
|
||
|
* if interfaceDecl: public <returntype> func(args);
|
||
|
* if !interfaceDecl: public <returntype> func(args) { body }
|
||
|
*/
|
||
|
void emitFunction(JFunc jfunc,
|
||
|
PrintStream out,
|
||
|
boolean nativeDecl, boolean interfaceDecl) {
|
||
|
boolean isPointerFunc =
|
||
|
jfunc.getName().endsWith("Pointer") &&
|
||
|
jfunc.getCFunc().hasPointerArg();
|
||
|
|
||
|
if (!nativeDecl && !interfaceDecl && !isPointerFunc) {
|
||
|
// If it's not a pointer function, we've already emitted it
|
||
|
// with nativeDecl == true
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (isPointerFunc) {
|
||
|
out.println(indent +
|
||
|
(nativeDecl ? "private native " :
|
||
|
(interfaceDecl ? "" : "public ")) +
|
||
|
jfunc.getType() + " " +
|
||
|
jfunc.getName() +
|
||
|
(nativeDecl ? "Bounds" : "") +
|
||
|
"(");
|
||
|
} else {
|
||
|
out.println(indent +
|
||
|
(nativeDecl ? "public native " :
|
||
|
(interfaceDecl ? "" : "public ")) +
|
||
|
jfunc.getType() + " " +
|
||
|
jfunc.getName() +
|
||
|
"(");
|
||
|
}
|
||
|
|
||
|
int numArgs = jfunc.getNumArgs();
|
||
|
for (int i = 0; i < numArgs; i++) {
|
||
|
String argName = jfunc.getArgName(i);
|
||
|
JType argType = jfunc.getArgType(i);
|
||
|
|
||
|
out.print(indent + indent + argType + " " + argName);
|
||
|
if (i == numArgs - 1) {
|
||
|
if (isPointerFunc && nativeDecl) {
|
||
|
out.println(",");
|
||
|
out.println(indent + indent + "int remaining");
|
||
|
} else {
|
||
|
out.println();
|
||
|
}
|
||
|
} else {
|
||
|
out.println(",");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (nativeDecl || interfaceDecl) {
|
||
|
out.println(indent + ");");
|
||
|
} else {
|
||
|
out.println(indent + ") {");
|
||
|
|
||
|
String iii = indent + indent;
|
||
|
|
||
|
String fname = jfunc.getName();
|
||
|
if (isPointerFunc) {
|
||
|
// TODO - deal with VBO variants
|
||
|
if (fname.equals("glColorPointer")) {
|
||
|
out.println(iii + "if ((size == 4) &&");
|
||
|
out.println(iii + " ((type == GL_FLOAT) ||");
|
||
|
out.println(iii + " (type == GL_UNSIGNED_BYTE) ||");
|
||
|
out.println(iii + " (type == GL_FIXED)) &&");
|
||
|
out.println(iii + " (stride >= 0)) {");
|
||
|
out.println(iii + indent + "_colorPointer = pointer;");
|
||
|
out.println(iii + "}");
|
||
|
} else if (fname.equals("glNormalPointer")) {
|
||
|
out.println(iii + "if (((type == GL_FLOAT) ||");
|
||
|
out.println(iii + " (type == GL_BYTE) ||");
|
||
|
out.println(iii + " (type == GL_SHORT) ||");
|
||
|
out.println(iii + " (type == GL_FIXED)) &&");
|
||
|
out.println(iii + " (stride >= 0)) {");
|
||
|
out.println(iii + indent + "_normalPointer = pointer;");
|
||
|
out.println(iii + "}");
|
||
|
} else if (fname.equals("glTexCoordPointer")) {
|
||
|
out.println(iii + "if (((size == 2) ||");
|
||
|
out.println(iii + " (size == 3) ||");
|
||
|
out.println(iii + " (size == 4)) &&");
|
||
|
out.println(iii + " ((type == GL_FLOAT) ||");
|
||
|
out.println(iii + " (type == GL_BYTE) ||");
|
||
|
out.println(iii + " (type == GL_SHORT) ||");
|
||
|
out.println(iii + " (type == GL_FIXED)) &&");
|
||
|
out.println(iii + " (stride >= 0)) {");
|
||
|
out.println(iii + indent + "_texCoordPointer = pointer;");
|
||
|
out.println(iii + "}");
|
||
|
} else if (fname.equals("glVertexPointer")) {
|
||
|
out.println(iii + "if (((size == 2) ||");
|
||
|
out.println(iii + " (size == 3) ||");
|
||
|
out.println(iii + " (size == 4)) &&");
|
||
|
out.println(iii + " ((type == GL_FLOAT) ||");
|
||
|
out.println(iii + " (type == GL_BYTE) ||");
|
||
|
out.println(iii + " (type == GL_SHORT) ||");
|
||
|
out.println(iii + " (type == GL_FIXED)) &&");
|
||
|
out.println(iii + " (stride >= 0)) {");
|
||
|
out.println(iii + indent + "_vertexPointer = pointer;");
|
||
|
out.println(iii + "}");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// emitBoundsChecks(jfunc, out, iii);
|
||
|
emitFunctionCall(jfunc, out, iii, false);
|
||
|
|
||
|
boolean isVoid = jfunc.getType().isVoid();
|
||
|
|
||
|
if (!isVoid) {
|
||
|
out.println(indent + indent + "return _returnValue;");
|
||
|
}
|
||
|
out.println(indent + "}");
|
||
|
}
|
||
|
out.println();
|
||
|
}
|
||
|
|
||
|
public static String getJniName(JType jType) {
|
||
|
String jniName = "";
|
||
|
if (jType.isClass()) {
|
||
|
return "L" + jType.getBaseType() + ";";
|
||
|
} else if (jType.isArray()) {
|
||
|
jniName = "[";
|
||
|
}
|
||
|
|
||
|
String baseType = jType.getBaseType();
|
||
|
if (baseType.equals("int")) {
|
||
|
jniName += "I";
|
||
|
} else if (baseType.equals("float")) {
|
||
|
jniName += "F";
|
||
|
} else if (baseType.equals("boolean")) {
|
||
|
jniName += "Z";
|
||
|
} else if (baseType.equals("short")) {
|
||
|
jniName += "S";
|
||
|
} else if (baseType.equals("long")) {
|
||
|
jniName += "L";
|
||
|
} else if (baseType.equals("byte")) {
|
||
|
jniName += "B";
|
||
|
}
|
||
|
return jniName;
|
||
|
}
|
||
|
|
||
|
String getJniType(JType jType) {
|
||
|
if (jType.isVoid()) {
|
||
|
return "void";
|
||
|
}
|
||
|
|
||
|
String baseType = jType.getBaseType();
|
||
|
if (jType.isPrimitive()) {
|
||
|
if (baseType.equals("String")) {
|
||
|
return "jstring";
|
||
|
} else {
|
||
|
return "j" + baseType;
|
||
|
}
|
||
|
} else if (jType.isArray()) {
|
||
|
return "j" + baseType + "Array";
|
||
|
} else {
|
||
|
return "jobject";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
String getJniMangledName(String name) {
|
||
|
name = name.replaceAll("_", "_1");
|
||
|
name = name.replaceAll(";", "_2");
|
||
|
name = name.replaceAll("\\[", "_3");
|
||
|
return name;
|
||
|
}
|
||
|
|
||
|
public void emitJniCode(JFunc jfunc, PrintStream out) {
|
||
|
CFunc cfunc = jfunc.getCFunc();
|
||
|
|
||
|
// Emit comment identifying original C function
|
||
|
//
|
||
|
// Example:
|
||
|
//
|
||
|
// /* void glClipPlanef ( GLenum plane, const GLfloat *equation ) */
|
||
|
//
|
||
|
out.println("/* " + cfunc.getOriginal() + " */");
|
||
|
|
||
|
// Emit JNI signature (name)
|
||
|
//
|
||
|
// Example:
|
||
|
//
|
||
|
// void
|
||
|
// android_glClipPlanef__I_3FI
|
||
|
//
|
||
|
|
||
|
String outName = "android_" + jfunc.getName();
|
||
|
boolean isPointerFunc = outName.endsWith("Pointer") &&
|
||
|
jfunc.getCFunc().hasPointerArg();
|
||
|
boolean isVBOPointerFunc = (outName.endsWith("Pointer") ||
|
||
|
outName.endsWith("DrawElements")) &&
|
||
|
!jfunc.getCFunc().hasPointerArg();
|
||
|
if (isPointerFunc) {
|
||
|
outName += "Bounds";
|
||
|
}
|
||
|
|
||
|
out.print("static ");
|
||
|
out.println(getJniType(jfunc.getType()));
|
||
|
out.print(outName);
|
||
|
|
||
|
String rsignature = getJniName(jfunc.getType());
|
||
|
|
||
|
String signature = "";
|
||
|
int numArgs = jfunc.getNumArgs();
|
||
|
for (int i = 0; i < numArgs; i++) {
|
||
|
JType argType = jfunc.getArgType(i);
|
||
|
signature += getJniName(argType);
|
||
|
}
|
||
|
if (isPointerFunc) {
|
||
|
signature += "I";
|
||
|
}
|
||
|
|
||
|
// Append signature to function name
|
||
|
String sig = getJniMangledName(signature).replace('.', '_');
|
||
|
out.print("__" + sig);
|
||
|
outName += "__" + sig;
|
||
|
|
||
|
signature = signature.replace('.', '/');
|
||
|
rsignature = rsignature.replace('.', '/');
|
||
|
|
||
|
out.println();
|
||
|
if (rsignature.length() == 0) {
|
||
|
rsignature = "V";
|
||
|
}
|
||
|
|
||
|
String s = "{\"" +
|
||
|
jfunc.getName() +
|
||
|
(isPointerFunc ? "Bounds" : "") +
|
||
|
"\", \"(" + signature +")" +
|
||
|
rsignature +
|
||
|
"\", (void *) " +
|
||
|
outName +
|
||
|
" },";
|
||
|
nativeRegistrations.add(s);
|
||
|
|
||
|
List<Integer> nonPrimitiveArgs = new ArrayList<Integer>();
|
||
|
int numBufferArgs = 0;
|
||
|
List<String> bufferArgNames = new ArrayList<String>();
|
||
|
|
||
|
// Emit JNI signature (arguments)
|
||
|
//
|
||
|
// Example:
|
||
|
//
|
||
|
// (JNIEnv *_env, jobject this, jint plane, jfloatArray equation_ref, jint offset) {
|
||
|
//
|
||
|
out.print(" (JNIEnv *_env, jobject _this");
|
||
|
for (int i = 0; i < numArgs; i++) {
|
||
|
out.print(", ");
|
||
|
JType argType = jfunc.getArgType(i);
|
||
|
String suffix;
|
||
|
if (!argType.isPrimitive()) {
|
||
|
if (argType.isArray()) {
|
||
|
suffix = "_ref";
|
||
|
} else {
|
||
|
suffix = "_buf";
|
||
|
}
|
||
|
nonPrimitiveArgs.add(new Integer(i));
|
||
|
if (jfunc.getArgType(i).isBuffer()) {
|
||
|
int cIndex = jfunc.getArgCIndex(i);
|
||
|
String cname = cfunc.getArgName(cIndex);
|
||
|
bufferArgNames.add(cname);
|
||
|
numBufferArgs++;
|
||
|
}
|
||
|
} else {
|
||
|
suffix = "";
|
||
|
}
|
||
|
|
||
|
out.print(getJniType(argType) + " " + jfunc.getArgName(i) + suffix);
|
||
|
}
|
||
|
if (isPointerFunc) {
|
||
|
out.print(", jint remaining");
|
||
|
}
|
||
|
out.println(") {");
|
||
|
|
||
|
int numArrays = 0;
|
||
|
int numBuffers = 0;
|
||
|
for (int i = 0; i < nonPrimitiveArgs.size(); i++) {
|
||
|
int idx = nonPrimitiveArgs.get(i).intValue();
|
||
|
int cIndex = jfunc.getArgCIndex(idx);
|
||
|
String cname = cfunc.getArgName(cIndex);
|
||
|
if (jfunc.getArgType(idx).isArray()) {
|
||
|
++numArrays;
|
||
|
}
|
||
|
if (jfunc.getArgType(idx).isBuffer()) {
|
||
|
++numBuffers;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Emit method body
|
||
|
|
||
|
// Emit local variable declarations for _exception and _returnValue
|
||
|
//
|
||
|
// Example:
|
||
|
//
|
||
|
// android::gl::ogles_context_t *ctx;
|
||
|
//
|
||
|
// jint _exception;
|
||
|
// GLenum _returnValue;
|
||
|
//
|
||
|
CType returnType = cfunc.getType();
|
||
|
boolean isVoid = returnType.isVoid();
|
||
|
|
||
|
boolean isUnsupported = isUnsupportedFunc(cfunc);
|
||
|
if (isUnsupported) {
|
||
|
out.println(indent +
|
||
|
"_env->ThrowNew(UOEClass,");
|
||
|
out.println(indent +
|
||
|
" \"" + cfunc.getName() + "\");");
|
||
|
if (!isVoid) {
|
||
|
String retval = getErrorReturnValue(cfunc);
|
||
|
out.println(indent + "return " + retval + ";");
|
||
|
}
|
||
|
out.println("}");
|
||
|
out.println();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (mUseContextPointer) {
|
||
|
out.println(indent +
|
||
|
"android::gl::ogles_context_t *ctx = getContext(_env, _this);");
|
||
|
}
|
||
|
|
||
|
boolean emitExceptionCheck = (numArrays > 0 || numBuffers > 0) &&
|
||
|
hasNonConstArg(jfunc, cfunc, nonPrimitiveArgs);
|
||
|
// mChecker.getChecks(cfunc.getName()) != null
|
||
|
|
||
|
// Emit an _exeption variable if there will be error checks
|
||
|
if (emitExceptionCheck) {
|
||
|
out.println(indent + "jint _exception = 0;");
|
||
|
}
|
||
|
|
||
|
// Emit a single _array or multiple _XXXArray variables
|
||
|
if (numBufferArgs == 1) {
|
||
|
out.println(indent + "jarray _array = (jarray) 0;");
|
||
|
} else {
|
||
|
for (int i = 0; i < numBufferArgs; i++) {
|
||
|
out.println(indent + "jarray _" + bufferArgNames.get(i) +
|
||
|
"Array = (jarray) 0;");
|
||
|
}
|
||
|
}
|
||
|
if (!isVoid) {
|
||
|
String retval = getErrorReturnValue(cfunc);
|
||
|
if (retval != null) {
|
||
|
out.println(indent + returnType.getDeclaration() +
|
||
|
" _returnValue = " + retval + ";");
|
||
|
} else {
|
||
|
out.println(indent + returnType.getDeclaration() +
|
||
|
" _returnValue;");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Emit local variable declarations for pointer arguments
|
||
|
//
|
||
|
// Example:
|
||
|
//
|
||
|
// GLfixed *eqn_base;
|
||
|
// GLfixed *eqn;
|
||
|
//
|
||
|
String offset = "offset";
|
||
|
String remaining = "_remaining";
|
||
|
if (nonPrimitiveArgs.size() > 0) {
|
||
|
for (int i = 0; i < nonPrimitiveArgs.size(); i++) {
|
||
|
int idx = nonPrimitiveArgs.get(i).intValue();
|
||
|
int cIndex = jfunc.getArgCIndex(idx);
|
||
|
String cname = cfunc.getArgName(cIndex);
|
||
|
|
||
|
CType type = cfunc.getArgType(jfunc.getArgCIndex(idx));
|
||
|
String decl = type.getDeclaration();
|
||
|
if (jfunc.getArgType(idx).isArray()) {
|
||
|
out.println(indent +
|
||
|
decl +
|
||
|
(decl.endsWith("*") ? "" : " ") +
|
||
|
jfunc.getArgName(idx) +
|
||
|
"_base = (" + decl + ") 0;");
|
||
|
}
|
||
|
remaining = (numArrays <= 1 && numBuffers <= 1) ? "_remaining" :
|
||
|
"_" + cname + "Remaining";
|
||
|
out.println(indent +
|
||
|
"jint " + remaining + ";");
|
||
|
out.println(indent +
|
||
|
decl +
|
||
|
(decl.endsWith("*") ? "" : " ") +
|
||
|
jfunc.getArgName(idx) +
|
||
|
" = (" + decl + ") 0;");
|
||
|
}
|
||
|
|
||
|
out.println();
|
||
|
}
|
||
|
|
||
|
String retval = isVoid ? "" : " _returnValue";
|
||
|
|
||
|
// Emit 'GetPrimitiveArrayCritical' for arrays
|
||
|
// Emit 'GetPointer' calls for Buffer pointers
|
||
|
int bufArgIdx = 0;
|
||
|
if (nonPrimitiveArgs.size() > 0) {
|
||
|
for (int i = 0; i < nonPrimitiveArgs.size(); i++) {
|
||
|
int idx = nonPrimitiveArgs.get(i).intValue();
|
||
|
int cIndex = jfunc.getArgCIndex(idx);
|
||
|
|
||
|
String cname = cfunc.getArgName(cIndex);
|
||
|
offset = numArrays <= 1 ? "offset" :
|
||
|
cname + "Offset";
|
||
|
remaining = (numArrays <= 1 && numBuffers <= 1) ? "_remaining" :
|
||
|
"_" + cname + "Remaining";
|
||
|
|
||
|
if (jfunc.getArgType(idx).isArray()) {
|
||
|
out.println(indent +
|
||
|
"if (!" +
|
||
|
cname +
|
||
|
"_ref) {");
|
||
|
if (emitExceptionCheck) {
|
||
|
out.println(indent + indent + "_exception = 1;");
|
||
|
}
|
||
|
out.println(indent + " " +
|
||
|
(mUseCPlusPlus ? "_env" : "(*_env)") +
|
||
|
"->ThrowNew(" +
|
||
|
(mUseCPlusPlus ? "" : "_env, ") +
|
||
|
"IAEClass, " +
|
||
|
"\"" + cname +
|
||
|
" == null\");");
|
||
|
out.println(indent + " goto exit;");
|
||
|
needsExit = true;
|
||
|
out.println(indent + "}");
|
||
|
|
||
|
out.println(indent + "if (" + offset + " < 0) {");
|
||
|
if (emitExceptionCheck) {
|
||
|
out.println(indent + indent + "_exception = 1;");
|
||
|
}
|
||
|
out.println(indent + " " +
|
||
|
(mUseCPlusPlus ? "_env" : "(*_env)") +
|
||
|
"->ThrowNew(" +
|
||
|
(mUseCPlusPlus ? "" : "_env, ") +
|
||
|
"IAEClass, " +
|
||
|
"\"" + offset + " < 0\");");
|
||
|
out.println(indent + " goto exit;");
|
||
|
needsExit = true;
|
||
|
out.println(indent + "}");
|
||
|
|
||
|
out.println(indent + remaining + " = " +
|
||
|
(mUseCPlusPlus ? "_env" : "(*_env)") +
|
||
|
"->GetArrayLength(" +
|
||
|
(mUseCPlusPlus ? "" : "_env, ") +
|
||
|
cname + "_ref) - " + offset + ";");
|
||
|
|
||
|
emitNativeBoundsChecks(cfunc, cname, out, false,
|
||
|
emitExceptionCheck,
|
||
|
offset, remaining, " ");
|
||
|
|
||
|
out.println(indent +
|
||
|
cname +
|
||
|
"_base = (" +
|
||
|
cfunc.getArgType(cIndex).getDeclaration() +
|
||
|
")");
|
||
|
out.println(indent + " " +
|
||
|
(mUseCPlusPlus ? "_env" : "(*_env)") +
|
||
|
"->GetPrimitiveArrayCritical(" +
|
||
|
(mUseCPlusPlus ? "" : "_env, ") +
|
||
|
jfunc.getArgName(idx) +
|
||
|
"_ref, (jboolean *)0);");
|
||
|
out.println(indent +
|
||
|
cname + " = " + cname + "_base + " + offset +
|
||
|
";");
|
||
|
out.println();
|
||
|
} else {
|
||
|
String array = numBufferArgs <= 1 ? "_array" :
|
||
|
"_" + bufferArgNames.get(bufArgIdx++) + "Array";
|
||
|
|
||
|
boolean nullAllowed = isNullAllowed(cfunc);
|
||
|
if (nullAllowed) {
|
||
|
out.println(indent + "if (" + cname + "_buf) {");
|
||
|
out.print(indent);
|
||
|
}
|
||
|
|
||
|
out.println(indent +
|
||
|
cname +
|
||
|
" = (" +
|
||
|
cfunc.getArgType(cIndex).getDeclaration() +
|
||
|
")getPointer(_env, " +
|
||
|
cname +
|
||
|
"_buf, &" + array + ", &" + remaining + ");");
|
||
|
|
||
|
if (nullAllowed) {
|
||
|
out.println(indent + "}");
|
||
|
}
|
||
|
|
||
|
emitNativeBoundsChecks(cfunc, cname, out, true,
|
||
|
emitExceptionCheck,
|
||
|
offset, remaining, " ");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!isVoid) {
|
||
|
out.print(indent + "_returnValue = ");
|
||
|
} else {
|
||
|
out.print(indent);
|
||
|
}
|
||
|
String name = cfunc.getName();
|
||
|
|
||
|
if (mUseContextPointer) {
|
||
|
name = name.substring(2, name.length()); // Strip off 'gl' prefix
|
||
|
name = name.substring(0, 1).toLowerCase() +
|
||
|
name.substring(1, name.length());
|
||
|
out.print("ctx->procs.");
|
||
|
}
|
||
|
|
||
|
out.print(name + (isPointerFunc ? "Bounds" : "") + "(");
|
||
|
|
||
|
numArgs = cfunc.getNumArgs();
|
||
|
if (numArgs == 0) {
|
||
|
if (mUseContextPointer) {
|
||
|
out.println("ctx);");
|
||
|
} else {
|
||
|
out.println(");");
|
||
|
}
|
||
|
} else {
|
||
|
if (mUseContextPointer) {
|
||
|
out.println("ctx,");
|
||
|
} else {
|
||
|
out.println();
|
||
|
}
|
||
|
for (int i = 0; i < numArgs; i++) {
|
||
|
String typecast;
|
||
|
if (i == numArgs - 1 && isVBOPointerFunc) {
|
||
|
typecast = "const GLvoid *";
|
||
|
} else {
|
||
|
typecast = cfunc.getArgType(i).getDeclaration();
|
||
|
}
|
||
|
out.print(indent + indent +
|
||
|
"(" +
|
||
|
typecast +
|
||
|
")" +
|
||
|
cfunc.getArgName(i));
|
||
|
|
||
|
if (i == numArgs - 1) {
|
||
|
if (isPointerFunc) {
|
||
|
out.println(",");
|
||
|
out.println(indent + indent + "(GLsizei)remaining");
|
||
|
} else {
|
||
|
out.println();
|
||
|
}
|
||
|
} else {
|
||
|
out.println(",");
|
||
|
}
|
||
|
}
|
||
|
out.println(indent + ");");
|
||
|
}
|
||
|
|
||
|
if (needsExit) {
|
||
|
out.println();
|
||
|
out.println("exit:");
|
||
|
needsExit = false;
|
||
|
}
|
||
|
|
||
|
bufArgIdx = 0;
|
||
|
if (nonPrimitiveArgs.size() > 0) {
|
||
|
for (int i = nonPrimitiveArgs.size() - 1; i >= 0; i--) {
|
||
|
int idx = nonPrimitiveArgs.get(i).intValue();
|
||
|
|
||
|
int cIndex = jfunc.getArgCIndex(idx);
|
||
|
if (jfunc.getArgType(idx).isArray()) {
|
||
|
|
||
|
// If the argument is 'const', GL will not write to it.
|
||
|
// In this case, we can use the 'JNI_ABORT' flag to avoid
|
||
|
// the need to write back to the Java array
|
||
|
out.println(indent +
|
||
|
"if (" + jfunc.getArgName(idx) + "_base) {");
|
||
|
out.println(indent + indent +
|
||
|
(mUseCPlusPlus ? "_env" : "(*_env)") +
|
||
|
"->ReleasePrimitiveArrayCritical(" +
|
||
|
(mUseCPlusPlus ? "" : "_env, ") +
|
||
|
jfunc.getArgName(idx) + "_ref, " +
|
||
|
cfunc.getArgName(cIndex) +
|
||
|
"_base,");
|
||
|
out.println(indent + indent + indent +
|
||
|
(cfunc.getArgType(cIndex).isConst() ?
|
||
|
"JNI_ABORT" :
|
||
|
"_exception ? JNI_ABORT: 0") +
|
||
|
");");
|
||
|
out.println(indent + "}");
|
||
|
} else if (jfunc.getArgType(idx).isBuffer()) {
|
||
|
String array = numBufferArgs <= 1 ? "_array" :
|
||
|
"_" + bufferArgNames.get(bufArgIdx++) + "Array";
|
||
|
out.println(indent + "if (" + array + ") {");
|
||
|
out.println(indent + indent +
|
||
|
"releasePointer(_env, " + array + ", " +
|
||
|
cfunc.getArgName(cIndex) +
|
||
|
", " +
|
||
|
(cfunc.getArgType(cIndex).isConst() ?
|
||
|
"JNI_FALSE" : "_exception ? JNI_FALSE : JNI_TRUE") +
|
||
|
");");
|
||
|
out.println(indent + "}");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!isVoid) {
|
||
|
out.println(indent + "return _returnValue;");
|
||
|
}
|
||
|
|
||
|
out.println("}");
|
||
|
out.println();
|
||
|
}
|
||
|
|
||
|
public void addNativeRegistration(String s) {
|
||
|
nativeRegistrations.add(s);
|
||
|
}
|
||
|
|
||
|
public void emitNativeRegistration() {
|
||
|
mCStream.println("static const char *classPathName = \"" +
|
||
|
mClassPathName +
|
||
|
"\";");
|
||
|
mCStream.println();
|
||
|
|
||
|
mCStream.println("static JNINativeMethod methods[] = {");
|
||
|
|
||
|
mCStream.println("{\"_nativeClassInit\", \"()V\", (void*)nativeClassInit },");
|
||
|
|
||
|
Iterator<String> i = nativeRegistrations.iterator();
|
||
|
while (i.hasNext()) {
|
||
|
mCStream.println(i.next());
|
||
|
}
|
||
|
|
||
|
mCStream.println("};");
|
||
|
mCStream.println();
|
||
|
|
||
|
|
||
|
mCStream.println("int register_com_google_android_gles_jni_GLImpl(JNIEnv *_env)");
|
||
|
mCStream.println("{");
|
||
|
mCStream.println(indent +
|
||
|
"int err;");
|
||
|
|
||
|
mCStream.println(indent +
|
||
|
"err = android::AndroidRuntime::registerNativeMethods(_env, classPathName, methods, NELEM(methods));");
|
||
|
|
||
|
mCStream.println(indent + "return err;");
|
||
|
mCStream.println("}");
|
||
|
}
|
||
|
}
|