/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
public class JniCodeEmitter {
static final boolean mUseCPlusPlus = true;
protected boolean mUseContextPointer = true;
protected boolean mUseStaticMethods = false;
protected boolean mUseSimpleMethodNames = false;
protected boolean mUseHideCommentForAPI = false;
protected String mClassPathName;
protected ParameterChecker mChecker;
protected List<String> nativeRegistrations = new ArrayList<String>();
boolean needsExit;
protected static String indent = " ";
HashSet<String> mFunctionsEmitted = new HashSet<String>();
public static String getJniName(JType jType) {
String jniName = "";
if (jType.isEGLHandle()) {
return (jType.isArray() ? "[" : "" ) + "Landroid/opengl/" + jType.getBaseType() + ";";
} else 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 += "J";
} else if (baseType.equals("byte")) {
jniName += "B";
} else if (baseType.equals("String")) {
jniName += "Ljava/lang/String;";
} else if (baseType.equals("void")) {
// nothing.
} else {
throw new RuntimeException("Unknown primitive basetype " + baseType);
}
return jniName;
}
public void emitCode(CFunc cfunc, String original,
PrintStream javaInterfaceStream,
PrintStream javaImplStream,
PrintStream cStream) {
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, javaImplStream);
emitJavaCode(jfunc, javaImplStream);
}
if (javaInterfaceStream != null) {
emitJavaInterfaceCode(jfunc, javaInterfaceStream);
}
if (!duplicate) {
emitJniCode(jfunc, cStream);
}
// Don't create IOBuffer versions of the EGL functions
if (cfunc.hasEGLHandleArg()) {
return;
}
}
jfunc = JFunc.convert(cfunc, false);
signature = jfunc.toString();
duplicate = false;
if (mFunctionsEmitted.contains(signature)) {
duplicate = true;
} else {
mFunctionsEmitted.add(signature);
}
if (!duplicate) {
emitNativeDeclaration(jfunc, javaImplStream);
}
if (javaInterfaceStream != null) {
emitJavaInterfaceCode(jfunc, javaInterfaceStream);
}
if (!duplicate) {
emitJavaCode(jfunc, javaImplStream);
emitJniCode(jfunc, cStream);
}
}
public void emitNativeDeclaration(JFunc jfunc, PrintStream out) {
if (mUseHideCommentForAPI) {
out.println(" /* @hide C function " + jfunc.getCFunc().getOriginal() + " */");
out.println();
} else {
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);
}
boolean isPointerFunc(JFunc jfunc) {
String name = jfunc.getName();
return (name.endsWith("Pointer") || name.endsWith("PointerOES"))
&& jfunc.getCFunc().hasPointerArg();
}
void emitFunctionCall(JFunc jfunc, PrintStream out, String iii, boolean grabArray) {
boolean isVoid = jfunc.getType().isVoid();
boolean isPointerFunc = isPointerFunc(jfunc);
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 = 1;");
out.println(iii + " break;");
out.println(iii + "}");
out.println(iii + "if (" + remaining + " < _needed) {");
out.println(iii + indent + "_exception = 1;");
out.println(iii + indent +
"_exceptionType = \"java/lang/IllegalArgumentException\";");
out.println(iii + indent +
"_exceptionMessage = \"" +
(isBuffer ? "remaining()" : "length - " + offset) +
" < needed\";");
out.println(iii + indent + "goto exit;");
out.println(iii + "}");
needsExit = true;
}
boolean isNullAllowed(CFunc cfunc) {
String[] checks = mChecker.getChecks(cfunc.getName());
int index = 1;
if (checks != null) {
while (index < checks.length) {
if (checks[index].equals("nullAllowed")) {
return true;
} else {
index = skipOneCheck(checks, index);
}
}
}
return false;
}
boolean hasCheckTest(CFunc cfunc) {
String[] checks = mChecker.getChecks(cfunc.getName());
int index = 1;
if (checks != null) {
while (index < checks.length) {
if (checks[index].startsWith("check")) {
return true;
} else {
index = skipOneCheck(checks, index);
}
}
}
return false;
}
boolean hasIfTest(CFunc cfunc) {
String[] checks = mChecker.getChecks(cfunc.getName());
int index = 1;
if (checks != null) {
while (index < checks.length) {
if (checks[index].startsWith("ifcheck")) {
return true;
} else {
index = skipOneCheck(checks, index);
}
}
}
return false;
}
int skipOneCheck(String[] checks, int index) {
if (checks[index].equals("return")) {
index += 2;
} else if (checks[index].startsWith("check")) {
index += 3;
} else if (checks[index].startsWith("sentinel")) {
index += 3;
} else if (checks[index].equals("ifcheck")) {
index += 5;
} else if (checks[index].equals("unsupported")) {
index += 1;
} else if (checks[index].equals("requires")) {
index += 2;
} else if (checks[index].equals("nullAllowed")) {
index += 1;
} else {
System.out.println("Error: unknown keyword \"" +
checks[index] + "\"");
System.exit(0);
}
return index;
}
String getErrorReturnValue(CFunc cfunc) {
CType returnType = cfunc.getType();
boolean isVoid = returnType.isVoid();
if (isVoid) {
return null;
}
if (returnType.getBaseType().startsWith("EGL")) {
return "(" + returnType.getDeclaration() + ") 0";
}
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 {
index = skipOneCheck(checks, index);
}
}
}
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 {
index = skipOneCheck(checks, index);
}
}
}
return false;
}
String isRequiresFunc(CFunc cfunc) {
String[] checks = mChecker.getChecks(cfunc.getName());
int index = 1;
if (checks != null) {
while (index < checks.length) {
if (checks[index].equals("requires")) {
return checks[index+1];
} else {
index = skipOneCheck(checks, index);
}
}
}
return null;
}
void emitNativeBoundsChecks(CFunc cfunc, String cname, PrintStream out,
boolean isBuffer, boolean emitExceptionCheck, String offset, String remaining, String iii) {
String[] checks = mChecker.getChecks(cfunc.getName());
boolean lastWasIfcheck = false;
int index = 1;
if (checks != null) {
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] + ") {");
out.println(iii + indent + "_exception = 1;");
String exceptionClassName = "java/lang/IllegalArgumentException";
// 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) {
String abbr = checks[index].substring(underscore + 1);
if (abbr.equals("AIOOBE")) {
exceptionClassName = "java/lang/ArrayIndexOutOfBoundsException";
} else {
throw new RuntimeException("unknown exception abbreviation: " + abbr);
}
}
out.println(iii + indent +
"_exceptionType = \""+exceptionClassName+"\";");
out.println(iii + indent +
"_exceptionMessage = \"" +
(isBuffer ? "remaining()" : "length - " +
offset) + " < " + checks[index + 2] +
" < needed\";");
out.println(iii + indent + "goto exit;");
out.println(iii + "}");
needsExit = true;
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 {
index = skipOneCheck(checks, index);
}
}
}
if (lastWasIfcheck) {
printIfcheckPostamble(out, isBuffer, emitExceptionCheck, iii);
}
}
void emitSentinelCheck(CFunc cfunc, String cname, PrintStream out,
boolean isBuffer, boolean emitExceptionCheck, String offset, String remaining, String iii) {
String[] checks = mChecker.getChecks(cfunc.getName());
int index = 1;
if (checks != null) {
while (index < checks.length) {
if (checks[index].startsWith("sentinel")) {
if (cname != null && !cname.equals(checks[index + 1])) {
index += 3;
continue;
}
out.println(iii + cname + "_sentinel = false;");
out.println(iii + "for (int i = " + remaining +
" - 1; i >= 0; i--) {");
out.println(iii + indent + "if (" + cname +
"[i] == " + checks[index + 2] + "){");
out.println(iii + indent + indent +
cname + "_sentinel = true;");
out.println(iii + indent + indent + "break;");
out.println(iii + indent + "}");
out.println(iii + "}");
out.println(iii +
"if (" + cname + "_sentinel == false) {");
out.println(iii + indent + "_exception = 1;");
out.println(iii + indent +
"_exceptionType = \"java/lang/IllegalArgumentException\";");
out.println(iii + indent + "_exceptionMessage = \"" + cname +
" must contain " + checks[index + 2] + "!\";");
out.println(iii + indent + "goto exit;");
out.println(iii + "}");
needsExit = true;
index += 3;
} else {
index = skipOneCheck(checks, index);
}
}
}
}
void emitLocalVariablesForSentinel(CFunc cfunc, PrintStream out) {
String[] checks = mChecker.getChecks(cfunc.getName());
int index = 1;
if (checks != null) {
while (index < checks.length) {
if (checks[index].startsWith("sentinel")) {
String cname = checks[index + 1];
out.println(indent + "bool " + cname + "_sentinel = false;");
index += 3;
} else {
index = skipOneCheck(checks, index);
}
}
}
}
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 = isPointerFunc(jfunc);
if (!nativeDecl && !interfaceDecl && !isPointerFunc) {
// If it's not a pointer function, we've already emitted it
// with nativeDecl == true
return;
}
String maybeStatic = mUseStaticMethods ? "static " : "";
if (isPointerFunc) {
out.println(indent +
(nativeDecl ? "private " + maybeStatic +"native " :
(interfaceDecl ? "" : "public ") + maybeStatic) +
jfunc.getType() + " " +
jfunc.getName() +
(nativeDecl ? "Bounds" : "") +
"(");
} else {
out.println(indent +
(nativeDecl ? "public " + maybeStatic +"native " :
(interfaceDecl ? "" : "public ") + maybeStatic) +
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;
// emitBoundsChecks(jfunc, out, iii);
emitFunctionCall(jfunc, out, iii, false);
// Set the pointer after we call the native code, so that if
// the native code throws an exception we don't modify the
// pointer. We assume that the native code is written so that
// if an exception is thrown, then the underlying glXXXPointer
// function will not have been called.
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 + "}");
} else if (fname.equals("glPointSizePointerOES")) {
out.println(iii + "if (((type == GL_FLOAT) ||");
out.println(iii + " (type == GL_FIXED)) &&");
out.println(iii + " (stride >= 0)) {");
out.println(iii + indent + "_pointSizePointerOES = pointer;");
out.println(iii + "}");
} else if (fname.equals("glMatrixIndexPointerOES")) {
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 + "_matrixIndexPointerOES = pointer;");
out.println(iii + "}");
} else if (fname.equals("glWeightPointer")) {
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 + "_weightPointerOES = pointer;");
out.println(iii + "}");
}
}
boolean isVoid = jfunc.getType().isVoid();
if (!isVoid) {
out.println(indent + indent + "return _returnValue;");
}
out.println(indent + "}");
}
out.println();
}
public void addNativeRegistration(String s) {
nativeRegistrations.add(s);
}
public void emitNativeRegistration(String registrationFunctionName,
PrintStream cStream) {
cStream.println("static const char *classPathName = \"" +
mClassPathName +
"\";");
cStream.println();
cStream.println("static JNINativeMethod methods[] = {");
cStream.println("{\"_nativeClassInit\", \"()V\", (void*)nativeClassInit },");
Iterator<String> i = nativeRegistrations.iterator();
while (i.hasNext()) {
cStream.println(i.next());
}
cStream.println("};");
cStream.println();
cStream.println("int " + registrationFunctionName + "(JNIEnv *_env)");
cStream.println("{");
cStream.println(indent +
"int err;");
cStream.println(indent +
"err = android::AndroidRuntime::registerNativeMethods(_env, classPathName, methods, NELEM(methods));");
cStream.println(indent + "return err;");
cStream.println("}");
}
public JniCodeEmitter() {
super();
}
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 jType.isClass() ? "jobjectArray" : "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 = isPointerFunc(jfunc);
boolean isPointerOffsetFunc =
(outName.endsWith("Pointer") || outName.endsWith("PointerOES") ||
outName.endsWith("glDrawElements") ||
outName.endsWith("glDrawRangeElements") ||
outName.endsWith("glTexImage2D") ||
outName.endsWith("glTexSubImage2D") ||
outName.endsWith("glCompressedTexImage2D") ||
outName.endsWith("glCompressedTexSubImage2D") ||
outName.endsWith("glTexImage3D") ||
outName.endsWith("glTexSubImage3D") ||
outName.endsWith("glCompressedTexImage3D") ||
outName.endsWith("glCompressedTexSubImage3D") ||
outName.endsWith("glReadPixels"))
&& !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('.', '_').replace('/', '_');
if (!mUseSimpleMethodNames) {
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>();
List<Integer> stringArgs = 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 if (argType.isBuffer()) {
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++;
}
}
if (argType.isString()) {
stringArgs.add(new Integer(i));
}
out.print(getJniType(argType) + " " + jfunc.getArgName(i) + suffix);
}
if (isPointerFunc) {
out.print(", jint remaining");
}
out.println(") {");
int numArrays = 0;
int numBuffers = 0;
int numStrings = 0;
for (int i = 0; i < nonPrimitiveArgs.size(); i++) {
int idx = nonPrimitiveArgs.get(i).intValue();
JType argType = jfunc.getArgType(idx);
if (argType.isArray()) {
++numArrays;
}
if (argType.isBuffer()) {
++numBuffers;
}
if (argType.isString()) {
++numStrings;
}
}
// 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 +
"jniThrowException(_env, \"java/lang/UnsupportedOperationException\",");
out.println(indent +
" \"" + cfunc.getName() + "\");");
if (!isVoid) {
String retval = getErrorReturnValue(cfunc);
if (cfunc.getType().isEGLHandle()) {
String baseType = cfunc.getType().getBaseType().toLowerCase();
out.println(indent +
"return toEGLHandle(_env, " + baseType + "Class, " +
baseType + "Constructor, " + retval + ");");
} else {
out.println(indent + "return " + retval + ";");
}
}
out.println("}");
out.println();
return;
}
String requiresExtension = isRequiresFunc(cfunc);
if (requiresExtension != null) {
out.println(indent +
"if (! supportsExtension(_env, _this, have_" + requiresExtension + "ID)) {");
out.println(indent + indent +
"jniThrowException(_env, \"java/lang/UnsupportedOperationException\",");
out.println(indent + indent +
" \"" + cfunc.getName() + "\");");
if (isVoid) {
out.println(indent + indent + " return;");
} else {
String retval = getErrorReturnValue(cfunc);
if (cfunc.getType().isEGLHandle()) {
String baseType = cfunc.getType().getBaseType().toLowerCase();
out.println(indent +
"return toEGLHandle(_env, " + baseType + "Class, " +
baseType + "Constructor, " + retval + ");");
} else {
out.println(indent + "return " + retval + ";");
}
}
out.println(indent + "}");
}
if (mUseContextPointer) {
out.println(indent +
"android::gl::ogles_context_t *ctx = getContext(_env, _this);");
}
boolean initializeReturnValue = stringArgs.size() > 0;
boolean emitExceptionCheck = ((numArrays > 0 || numStrings > 0)
&& (hasNonConstArg(jfunc, cfunc, nonPrimitiveArgs)
|| (cfunc.hasPointerArg() && numArrays > 0))
|| hasCheckTest(cfunc)
|| hasIfTest(cfunc))
|| (stringArgs.size() > 0);
// mChecker.getChecks(cfunc.getName()) != null
// Emit an _exeption variable if there will be error checks
if (emitExceptionCheck) {
out.println(indent + "jint _exception = 0;");
out.println(indent + "const char * _exceptionType = NULL;");
out.println(indent + "const char * _exceptionMessage = NULL;");
}
// Emit a single _array or multiple _XXXArray variables
if (numBufferArgs == 1) {
out.println(indent + "jarray _array = (jarray) 0;");
out.println(indent + "jint _bufferOffset = (jint) 0;");
} else {
for (int i = 0; i < numBufferArgs; i++) {
out.println(indent + "jarray _" + bufferArgNames.get(i) +
"Array = (jarray) 0;");
out.println(indent + "jint _" + bufferArgNames.get(i) +
"BufferOffset = (jint) 0;");
}
}
if (!isVoid) {
String retval = getErrorReturnValue(cfunc);
if (retval != null) {
out.println(indent + returnType.getDeclaration() +
" _returnValue = " + retval + ";");
} else if (initializeReturnValue) {
out.println(indent + returnType.getDeclaration() +
" _returnValue = 0;");
} else {
out.println(indent + returnType.getDeclaration() +
" _returnValue;");
}
}
// Emit local variable declarations for EGL Handles
//
// Example:
//
// EGLSurface surface_native = (EGLHandle)fromEGLHandle(_env, surfaceClass, surfaceConstructor, surface);
//
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);
if (jfunc.getArgType(idx).isBuffer()
|| jfunc.getArgType(idx).isArray()
|| !jfunc.getArgType(idx).isEGLHandle())
continue;
CType type = cfunc.getArgType(jfunc.getArgCIndex(idx));
String decl = type.getDeclaration();
out.println(indent +
decl + " " + cname + "_native = (" +
decl + ") fromEGLHandle(_env, " +
type.getBaseType().toLowerCase() +
"GetHandleID, " + jfunc.getArgName(idx) +
");");
}
}
// Emit local variable declarations for element/sentinel checks
//
// Example:
//
// bool attrib_list_sentinel_found = false;
//
emitLocalVariablesForSentinel(cfunc, out);
// 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);
if (!jfunc.getArgType(idx).isBuffer() && !jfunc.getArgType(idx).isArray())
continue;
CType type = cfunc.getArgType(jfunc.getArgCIndex(idx));
String decl = type.getDeclaration();
if (jfunc.getArgType(idx).isArray() && !jfunc.getArgType(idx).isClass()) {
out.println(indent +
decl +
(decl.endsWith("*") ? "" : " ") +
jfunc.getArgName(idx) +
"_base = (" + decl + ") 0;");
}
remaining = ((numArrays + numBuffers) <= 1) ? "_remaining" :
"_" + cname + "Remaining";
out.println(indent +
"jint " + remaining + ";");
out.println(indent +
decl +
(decl.endsWith("*") ? "" : " ") +
jfunc.getArgName(idx) +
" = (" + decl + ") 0;");
}
out.println();
}
// Emit local variable declaration for strings
if (stringArgs.size() > 0) {
for (int i = 0; i < stringArgs.size(); i++) {
int idx = stringArgs.get(i).intValue();
int cIndex = jfunc.getArgCIndex(idx);
String cname = cfunc.getArgName(cIndex);
out.println(indent + "const char* _native" + cname + " = 0;");
}
out.println();
}
// Null pointer checks and GetStringUTFChars
if (stringArgs.size() > 0) {
for (int i = 0; i < stringArgs.size(); i++) {
int idx = stringArgs.get(i).intValue();
int cIndex = jfunc.getArgCIndex(idx);
String cname = cfunc.getArgName(cIndex);
CType type = cfunc.getArgType(jfunc.getArgCIndex(idx));
String decl = type.getDeclaration();
needsExit = true;
out.println(indent + "if (!" + cname + ") {");
out.println(indent + indent + "_exception = 1;");
out.println(indent + indent +
"_exceptionType = \"java/lang/IllegalArgumentException\";");
out.println(indent + indent +
"_exceptionMessage = \"" + cname + " == null\";");
out.println(indent + indent + "goto exit;");
out.println(indent + "}");
out.println(indent + "_native" + cname + " = _env->GetStringUTFChars(" + cname + ", 0);");
}
out.println();
}
// Emit 'GetPrimitiveArrayCritical' for non-object arrays
// Emit 'GetPointer' calls for Buffer pointers
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 + numBuffers) <= 1) ? "_remaining" :
"_" + cname + "Remaining";
if (jfunc.getArgType(idx).isArray()
&& !jfunc.getArgType(idx).isEGLHandle()) {
needsExit = true;
out.println(indent + "if (!" + cname + "_ref) {");
out.println(indent + indent + "_exception = 1;");
out.println(indent + indent +
"_exceptionType = \"java/lang/IllegalArgumentException\";");
out.println(indent + indent +
"_exceptionMessage = \"" + cname +" == null\";");
out.println(indent + indent + "goto exit;");
out.println(indent + "}");
out.println(indent + "if (" + offset + " < 0) {");
out.println(indent + indent + "_exception = 1;");
out.println(indent + indent +
"_exceptionType = \"java/lang/IllegalArgumentException\";");
out.println(indent + indent +
"_exceptionMessage = \"" + offset +" < 0\";");
out.println(indent + indent + "goto exit;");
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 + ";");
emitSentinelCheck(cfunc, cname, out, false,
emitExceptionCheck, offset,
remaining, indent);
out.println();
} else if (jfunc.getArgType(idx).isArray()
&& jfunc.getArgType(idx).isEGLHandle()) {
needsExit = true;
out.println(indent + "if (!" + cname + "_ref) {");
out.println(indent + indent + "_exception = 1;");
out.println(indent + indent +
"_exceptionType = \"java/lang/IllegalArgumentException\";");
out.println(indent + indent + "_exceptionMessage = \"" + cname +" == null\";");
out.println(indent + indent + "goto exit;");
out.println(indent + "}");
out.println(indent + "if (" + offset + " < 0) {");
out.println(indent + indent + "_exception = 1;");
out.println(indent + indent +
"_exceptionType = \"java/lang/IllegalArgumentException\";");
out.println(indent + indent + "_exceptionMessage = \"" + offset +" < 0\";");
out.println(indent + indent + "goto exit;");
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 +
jfunc.getArgName(idx) + " = new " +
cfunc.getArgType(cIndex).getBaseType() +
"["+ remaining + "];");
out.println();
} else if (jfunc.getArgType(idx).isBuffer()) {
String array = numBufferArgs <= 1 ? "_array" :
"_" + cfunc.getArgName(cIndex) + "Array";
String bufferOffset = numBufferArgs <= 1 ? "_bufferOffset" :
"_" + cfunc.getArgName(cIndex) + "BufferOffset";
boolean nullAllowed = isNullAllowed(cfunc) || isPointerFunc;
if (nullAllowed) {
out.println(indent + "if (" + cname + "_buf) {");
out.print(indent);
}
if (isPointerFunc) {
out.println(indent +
cname +
" = (" +
cfunc.getArgType(cIndex).getDeclaration() +
") getDirectBufferPointer(_env, " +
cname + "_buf);");
String iii = " ";
out.println(iii + indent + "if ( ! " + cname + " ) {");
out.println(iii + indent + indent + "return;");
out.println(iii + indent + "}");
} else {
out.println(indent +
cname +
" = (" +
cfunc.getArgType(cIndex).getDeclaration() +
")getPointer(_env, " +
cname +
"_buf, &" + array + ", &" + remaining + ", &" + bufferOffset +
");");
}
emitNativeBoundsChecks(cfunc, cname, out, true,
emitExceptionCheck,
offset, remaining, nullAllowed ? " " : " ");
if (nullAllowed) {
out.println(indent + "}");
}
}
}
}
// Emit 'GetPrimitiveArrayCritical' for pointers if needed
if (nonPrimitiveArgs.size() > 0) {
for (int i = 0; i < nonPrimitiveArgs.size(); i++) {
int idx = nonPrimitiveArgs.get(i).intValue();
int cIndex = jfunc.getArgCIndex(idx);
if(!jfunc.getArgType(idx).isBuffer() || isPointerFunc) continue;
String cname = cfunc.getArgName(cIndex);
String bufferOffset = numBufferArgs <= 1 ? "_bufferOffset" :
"_" + cname + "BufferOffset";
String array = numBufferArgs <= 1 ? "_array" :
"_" + cfunc.getArgName(cIndex) + "Array";
boolean nullAllowed = isNullAllowed(cfunc) || isPointerFunc;
if (nullAllowed) {
out.println(indent + "if (" + cname + "_buf && " + cname +" == NULL) {");
} else {
out.println(indent + "if (" + cname +" == NULL) {");
}
out.println(indent + indent + "char * _" + cname + "Base = (char *)_env->GetPrimitiveArrayCritical(" + array + ", (jboolean *) 0);");
out.println(indent + indent + cname + " = (" +cfunc.getArgType(cIndex).getDeclaration() +") (_" + cname + "Base + " + bufferOffset + ");");
out.println(indent + "}");
}
}
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 && isPointerOffsetFunc) {
typecast = "reinterpret_cast<GLvoid *>";
} else {
typecast = "(" + cfunc.getArgType(i).getDeclaration() + ")";
}
out.print(indent + indent +
typecast);
if (cfunc.getArgType(i).isConstCharPointer()) {
out.print("_native");
}
if (cfunc.getArgType(i).isEGLHandle() &&
!cfunc.getArgType(i).isPointer()){
out.print(cfunc.getArgName(i)+"_native");
} else if (i == numArgs - 1 && isPointerOffsetFunc){
out.print("("+cfunc.getArgName(i)+")");
} else {
out.print(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;
}
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() && !jfunc.getArgType(idx).isClass()) {
// 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()) {
if (! isPointerFunc) {
String array = numBufferArgs <= 1 ? "_array" :
"_" + cfunc.getArgName(cIndex) + "Array";
out.println(indent + "if (" + array + ") {");
out.println(indent + indent +
"releasePointer(_env, " + array + ", " +
cfunc.getArgName(cIndex) +
", " +
(cfunc.getArgType(cIndex).isConst() ?
"JNI_FALSE" : (emitExceptionCheck ?
"_exception ? JNI_FALSE : JNI_TRUE" : "JNI_TRUE")) +
");");
out.println(indent + "}");
}
}
}
}
// Emit local variable declaration for strings
if (stringArgs.size() > 0) {
for (int i = 0; i < stringArgs.size(); i++) {
int idx = stringArgs.get(i).intValue();
int cIndex = jfunc.getArgCIndex(idx);
String cname = cfunc.getArgName(cIndex);
out.println(indent + "if (_native" + cname + ") {");
out.println(indent + " _env->ReleaseStringUTFChars(" + cname + ", _native" + cname + ");");
out.println(indent + "}");
}
out.println();
}
// Copy results back to java arrays
if (nonPrimitiveArgs.size() > 0) {
for (int i = nonPrimitiveArgs.size() - 1; i >= 0; i--) {
int idx = nonPrimitiveArgs.get(i).intValue();
int cIndex = jfunc.getArgCIndex(idx);
String baseType = cfunc.getArgType(cIndex).getBaseType().toLowerCase();
if (jfunc.getArgType(idx).isArray() && jfunc.getArgType(idx).isClass()) {
remaining = ((numArrays + numBuffers) <= 1) ? "_remaining" :
"_" + cfunc.getArgName(cIndex) + "Remaining";
offset = numArrays <= 1 ? "offset" : cfunc.getArgName(cIndex) + "Offset";
out.println(indent +
"if (" + jfunc.getArgName(idx) + ") {");
out.println(indent + indent +
"for (int i = 0; i < " + remaining + "; i++) {");
out.println(indent + indent + indent +
"jobject " + cfunc.getArgName(cIndex) +
"_new = toEGLHandle(_env, " + baseType +
"Class, " + baseType + "Constructor, " +
cfunc.getArgName(cIndex) + "[i]);");
out.println(indent + indent + indent +
(mUseCPlusPlus ? "_env" : "(*_env)") +
"->SetObjectArrayElement(" +
(mUseCPlusPlus ? "" : "_env, ") +
cfunc.getArgName(cIndex) +
"_ref, i + " + offset + ", " +
cfunc.getArgName(cIndex) + "_new);");
out.println(indent + indent + "}");
out.println(indent + indent +
"delete[] " + jfunc.getArgName(idx) + ";");
out.println(indent + "}");
}
}
}
// Throw exception if there is one
if (emitExceptionCheck) {
out.println(indent + "if (_exception) {");
out.println(indent + indent +
"jniThrowException(_env, _exceptionType, _exceptionMessage);");
out.println(indent + "}");
}
if (!isVoid) {
if (cfunc.getType().isEGLHandle()) {
String baseType = cfunc.getType().getBaseType().toLowerCase();
out.println(indent +
"return toEGLHandle(_env, " + baseType + "Class, " +
baseType + "Constructor, _returnValue);");
} else {
out.println(indent + "return (" +
getJniType(jfunc.getType()) + ")_returnValue;");
}
}
out.println("}");
out.println();
}
}