| 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 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.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"; |
| } else if (baseType.equals("String")) { |
| jniName += "Ljava/lang/String;"; |
| } else if (baseType.equals("void")) { |
| // nothing. |
| } else { |
| throw new RuntimeException("Uknown 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); |
| } |
| } |
| |
| 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) { |
| 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) { |
| |
| 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] + |
| ") {"); |
| 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; |
| } |
| |
| 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 + "}"); |
| } |
| } |
| |
| 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 "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('.', '_').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>(); |
| 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 { |
| 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 = ""; |
| } |
| 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 + |
| "_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 initializeReturnValue = stringArgs.size() > 0; |
| |
| boolean emitExceptionCheck = (numArrays > 0 || numBuffers > 0 || numStrings > 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 if (initializeReturnValue) { |
| out.println(indent + returnType.getDeclaration() + |
| " _returnValue = 0;"); |
| } 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 + 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(); |
| out.println(indent + "if (!" + cname + ") {"); |
| out.println(indent + " _env->ThrowNew(IAEClass, \"" + cname + " == null\");"); |
| out.println(indent + " goto exit;"); |
| needsExit = true; |
| out.println(indent + "}"); |
| |
| out.println(indent + "_native" + cname + " = _env->GetStringUTFChars(" + cname + ", 0);"); |
| } |
| |
| out.println(); |
| } |
| |
| // 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 + 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) || 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 + iii + indent + "return;"); |
| out.println(iii + indent + "}"); |
| } else { |
| out.println(indent + |
| cname + |
| " = (" + |
| cfunc.getArgType(cIndex).getDeclaration() + |
| ")getPointer(_env, " + |
| cname + |
| "_buf, &" + array + ", &" + remaining + |
| ");"); |
| } |
| |
| emitNativeBoundsChecks(cfunc, cname, out, true, |
| emitExceptionCheck, |
| offset, remaining, nullAllowed ? " " : " "); |
| |
| if (nullAllowed) { |
| 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 && isVBOPointerFunc) { |
| typecast = "const GLvoid *"; |
| } else { |
| typecast = cfunc.getArgType(i).getDeclaration(); |
| } |
| out.print(indent + indent + |
| "(" + |
| typecast + |
| ")"); |
| if (cfunc.getArgType(i).isConstCharPointer()) { |
| out.print("_native"); |
| } |
| 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; |
| } |
| |
| 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()) { |
| if (! isPointerFunc) { |
| 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 + "}"); |
| } |
| } |
| } |
| } |
| |
| // 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(); |
| } |
| |
| |
| if (!isVoid) { |
| out.println(indent + "return _returnValue;"); |
| } |
| |
| out.println("}"); |
| out.println(); |
| } |
| |
| } |