• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 import java.io.PrintStream;
2 import java.util.ArrayList;
3 import java.util.HashSet;
4 import java.util.Iterator;
5 import java.util.List;
6 
7 public class JniCodeEmitter {
8 
9     static final boolean mUseCPlusPlus = true;
10     protected boolean mUseContextPointer = true;
11     protected boolean mUseStaticMethods = false;
12     protected String mClassPathName;
13     protected ParameterChecker mChecker;
14     protected List<String> nativeRegistrations = new ArrayList<String>();
15     boolean needsExit;
16     protected static String indent = "    ";
17     HashSet<String> mFunctionsEmitted = new HashSet<String>();
18 
getJniName(JType jType)19     public static String getJniName(JType jType) {
20         String jniName = "";
21         if (jType.isClass()) {
22             return "L" + jType.getBaseType() + ";";
23         } else if (jType.isArray()) {
24             jniName = "[";
25         }
26 
27         String baseType = jType.getBaseType();
28         if (baseType.equals("int")) {
29             jniName += "I";
30         } else if (baseType.equals("float")) {
31             jniName += "F";
32         } else if (baseType.equals("boolean")) {
33             jniName += "Z";
34         } else if (baseType.equals("short")) {
35             jniName += "S";
36         } else if (baseType.equals("long")) {
37             jniName += "L";
38         } else if (baseType.equals("byte")) {
39             jniName += "B";
40         }
41         return jniName;
42     }
43 
44 
emitCode(CFunc cfunc, String original, PrintStream javaInterfaceStream, PrintStream javaImplStream, PrintStream cStream)45     public void emitCode(CFunc cfunc, String original,
46             PrintStream javaInterfaceStream,
47             PrintStream javaImplStream,
48             PrintStream cStream) {
49         JFunc jfunc;
50         String signature;
51         boolean duplicate;
52 
53         if (cfunc.hasTypedPointerArg()) {
54             jfunc = JFunc.convert(cfunc, true);
55 
56             // Don't emit duplicate functions
57             // These may appear because they are defined in multiple
58             // Java interfaces (e.g., GL11/GL11ExtensionPack)
59             signature = jfunc.toString();
60             duplicate = false;
61             if (mFunctionsEmitted.contains(signature)) {
62                 duplicate = true;
63             } else {
64                 mFunctionsEmitted.add(signature);
65             }
66 
67             if (!duplicate) {
68                 emitNativeDeclaration(jfunc, javaImplStream);
69                 emitJavaCode(jfunc, javaImplStream);
70             }
71             if (javaInterfaceStream != null) {
72                 emitJavaInterfaceCode(jfunc, javaInterfaceStream);
73             }
74             if (!duplicate) {
75                 emitJniCode(jfunc, cStream);
76             }
77         }
78 
79         jfunc = JFunc.convert(cfunc, false);
80 
81         signature = jfunc.toString();
82         duplicate = false;
83         if (mFunctionsEmitted.contains(signature)) {
84             duplicate = true;
85         } else {
86             mFunctionsEmitted.add(signature);
87         }
88 
89         if (!duplicate) {
90             emitNativeDeclaration(jfunc, javaImplStream);
91         }
92         if (javaInterfaceStream != null) {
93             emitJavaInterfaceCode(jfunc, javaInterfaceStream);
94         }
95         if (!duplicate) {
96             emitJavaCode(jfunc, javaImplStream);
97             emitJniCode(jfunc, cStream);
98         }
99     }
100 
emitNativeDeclaration(JFunc jfunc, PrintStream out)101     public void emitNativeDeclaration(JFunc jfunc, PrintStream out) {
102         out.println("    // C function " + jfunc.getCFunc().getOriginal());
103         out.println();
104 
105         emitFunction(jfunc, out, true, false);
106     }
107 
emitJavaInterfaceCode(JFunc jfunc, PrintStream out)108     public void emitJavaInterfaceCode(JFunc jfunc, PrintStream out) {
109         emitFunction(jfunc, out, false, true);
110     }
111 
emitJavaCode(JFunc jfunc, PrintStream out)112     public void emitJavaCode(JFunc jfunc, PrintStream out) {
113         emitFunction(jfunc, out, false, false);
114     }
115 
emitFunctionCall(JFunc jfunc, PrintStream out, String iii, boolean grabArray)116     void emitFunctionCall(JFunc jfunc, PrintStream out, String iii, boolean grabArray) {
117         boolean isVoid = jfunc.getType().isVoid();
118         boolean isPointerFunc = jfunc.getName().endsWith("Pointer") &&
119             jfunc.getCFunc().hasPointerArg();
120 
121         if (!isVoid) {
122             out.println(iii +
123                         jfunc.getType() + " _returnValue;");
124         }
125         out.println(iii +
126                     (isVoid ? "" : "_returnValue = ") +
127                     jfunc.getName() +
128                     (isPointerFunc ? "Bounds" : "" ) +
129                     "(");
130 
131         int numArgs = jfunc.getNumArgs();
132         for (int i = 0; i < numArgs; i++) {
133             String argName = jfunc.getArgName(i);
134             JType argType = jfunc.getArgType(i);
135 
136             if (grabArray && argType.isTypedBuffer()) {
137                 String typeName = argType.getBaseType();
138                 typeName = typeName.substring(9, typeName.length() - 6);
139                 out.println(iii + indent + "get" + typeName + "Array(" + argName + "),");
140                 out.print(iii + indent + "getOffset(" + argName + ")");
141             } else {
142                 out.print(iii + indent + argName);
143             }
144             if (i == numArgs - 1) {
145                 if (isPointerFunc) {
146                     out.println(",");
147                     out.println(iii + indent + argName + ".remaining()");
148                 } else {
149                     out.println();
150                 }
151             } else {
152                 out.println(",");
153             }
154         }
155 
156         out.println(iii + ");");
157     }
158 
printIfcheckPostamble(PrintStream out, boolean isBuffer, boolean emitExceptionCheck, String iii)159     void printIfcheckPostamble(PrintStream out, boolean isBuffer, boolean emitExceptionCheck,
160             String iii) {
161                 printIfcheckPostamble(out, isBuffer, emitExceptionCheck,
162                                       "offset", "_remaining", iii);
163             }
164 
printIfcheckPostamble(PrintStream out, boolean isBuffer, boolean emitExceptionCheck, String offset, String remaining, String iii)165     void printIfcheckPostamble(PrintStream out, boolean isBuffer, boolean emitExceptionCheck,
166             String offset, String remaining, String iii) {
167                 out.println(iii + "    default:");
168                 out.println(iii + "        _needed = 0;");
169                 out.println(iii + "        break;");
170                 out.println(iii + "}");
171 
172                 out.println(iii + "if (" + remaining + " < _needed) {");
173                 if (emitExceptionCheck) {
174                     out.println(iii + indent + "_exception = 1;");
175                 }
176                 out.println(iii + indent +
177                             (mUseCPlusPlus ? "_env" : "(*_env)") +
178                             "->ThrowNew(" +
179                             (mUseCPlusPlus ? "" : "_env, ") +
180                             "IAEClass, " +
181                             "\"" +
182                             (isBuffer ?
183                              "remaining()" : "length - " + offset) +
184                             " < needed\");");
185                 out.println(iii + indent + "goto exit;");
186                 needsExit = true;
187                 out.println(iii + "}");
188             }
189 
isNullAllowed(CFunc cfunc)190     boolean isNullAllowed(CFunc cfunc) {
191         String[] checks = mChecker.getChecks(cfunc.getName());
192         int index = 1;
193         if (checks != null) {
194             while (index < checks.length) {
195                 if (checks[index].equals("return")) {
196                     index += 2;
197                 } else if (checks[index].startsWith("check")) {
198                     index += 3;
199                 } else if (checks[index].equals("ifcheck")) {
200                     index += 5;
201                 } else if (checks[index].equals("unsupported")) {
202                     index += 1;
203                 } else if (checks[index].equals("nullAllowed")) {
204                     return true;
205                 } else {
206                     System.out.println("Error: unknown keyword \"" +
207                                        checks[index] + "\"");
208                     System.exit(0);
209                 }
210             }
211         }
212         return false;
213     }
214 
getErrorReturnValue(CFunc cfunc)215     String getErrorReturnValue(CFunc cfunc) {
216         CType returnType = cfunc.getType();
217         boolean isVoid = returnType.isVoid();
218         if (isVoid) {
219             return null;
220         }
221 
222         String[] checks = mChecker.getChecks(cfunc.getName());
223 
224         int index = 1;
225         if (checks != null) {
226             while (index < checks.length) {
227                 if (checks[index].equals("return")) {
228                     return checks[index + 1];
229                 } else if (checks[index].startsWith("check")) {
230                     index += 3;
231                 } else if (checks[index].equals("ifcheck")) {
232                     index += 5;
233                 } else if (checks[index].equals("unsupported")) {
234                     index += 1;
235                 } else if (checks[index].equals("nullAllowed")) {
236                     index += 1;
237                 } else {
238                     System.out.println("Error: unknown keyword \"" +
239                                        checks[index] + "\"");
240                     System.exit(0);
241                 }
242             }
243         }
244 
245         return null;
246     }
247 
isUnsupportedFunc(CFunc cfunc)248     boolean isUnsupportedFunc(CFunc cfunc) {
249         String[] checks = mChecker.getChecks(cfunc.getName());
250         int index = 1;
251         if (checks != null) {
252             while (index < checks.length) {
253                 if (checks[index].equals("unsupported")) {
254                     return true;
255                 } else if (checks[index].equals("return")) {
256                     index += 2;
257                 } else if (checks[index].startsWith("check")) {
258                     index += 3;
259                 } else if (checks[index].equals("ifcheck")) {
260                     index += 5;
261                 } else if (checks[index].equals("nullAllowed")) {
262                     index += 1;
263                 } else {
264                     System.out.println("Error: unknown keyword \"" +
265                                        checks[index] + "\"");
266                     System.exit(0);
267                 }
268             }
269         }
270         return false;
271     }
272 
emitNativeBoundsChecks(CFunc cfunc, String cname, PrintStream out, boolean isBuffer, boolean emitExceptionCheck, String offset, String remaining, String iii)273     void emitNativeBoundsChecks(CFunc cfunc, String cname, PrintStream out,
274             boolean isBuffer, boolean emitExceptionCheck, String offset, String remaining, String iii) {
275 
276                 String[] checks = mChecker.getChecks(cfunc.getName());
277 
278                 boolean lastWasIfcheck = false;
279 
280                 int index = 1;
281                 if (checks != null) {
282                     while (index < checks.length) {
283                         if (checks[index].startsWith("check")) {
284                             if (lastWasIfcheck) {
285                                 printIfcheckPostamble(out, isBuffer, emitExceptionCheck,
286                                                       offset, remaining, iii);
287                             }
288                             lastWasIfcheck = false;
289                             if (cname != null && !cname.equals(checks[index + 1])) {
290                                 index += 3;
291                                 continue;
292                             }
293                             out.println(iii + "if (" + remaining + " < " +
294                                         checks[index + 2] +
295                                         ") {");
296                             if (emitExceptionCheck) {
297                                 out.println(iii + indent + "_exception = 1;");
298                             }
299                     String exceptionClassName = "IAEClass";
300                     // If the "check" keyword was of the form
301                     // "check_<class name>", use the class name in the
302                     // exception to be thrown
303                     int underscore = checks[index].indexOf('_');
304                     if (underscore >= 0) {
305                     exceptionClassName = checks[index].substring(underscore + 1) + "Class";
306                     }
307                             out.println(iii + indent +
308                                         (mUseCPlusPlus ? "_env" : "(*_env)") +
309                                         "->ThrowNew(" +
310                                         (mUseCPlusPlus ? "" : "_env, ") +
311                         exceptionClassName + ", " +
312                                         "\"" +
313                                         (isBuffer ?
314                                          "remaining()" : "length - " + offset) +
315                                         " < " + checks[index + 2] +
316                                         "\");");
317 
318                             out.println(iii + indent + "goto exit;");
319                             needsExit = true;
320                             out.println(iii + "}");
321 
322                             index += 3;
323                         } else if (checks[index].equals("ifcheck")) {
324                             String[] matches = checks[index + 4].split(",");
325 
326                             if (!lastWasIfcheck) {
327                                 out.println(iii + "int _needed;");
328                                 out.println(iii +
329                                             "switch (" +
330                                             checks[index + 3] +
331                                             ") {");
332                             }
333 
334                             for (int i = 0; i < matches.length; i++) {
335                                 out.println("#if defined(" + matches[i] + ")");
336                                 out.println(iii +
337                                             "    case " +
338                                             matches[i] +
339                                             ":");
340                                 out.println("#endif // defined(" + matches[i] + ")");
341                             }
342                             out.println(iii +
343                                         "        _needed = " +
344                                         checks[index + 2] +
345                                         ";");
346                             out.println(iii +
347                                         "        break;");
348 
349                             lastWasIfcheck = true;
350                             index += 5;
351                         } else if (checks[index].equals("return")) {
352                             // ignore
353                             index += 2;
354                         } else if (checks[index].equals("unsupported")) {
355                             // ignore
356                             index += 1;
357                         } else if (checks[index].equals("nullAllowed")) {
358                             // ignore
359                             index += 1;
360                         } else {
361                             System.out.println("Error: unknown keyword \"" +
362                                                checks[index] + "\"");
363                             System.exit(0);
364                         }
365                     }
366                 }
367 
368                 if (lastWasIfcheck) {
369                     printIfcheckPostamble(out, isBuffer, emitExceptionCheck, iii);
370                 }
371             }
372 
hasNonConstArg(JFunc jfunc, CFunc cfunc, List<Integer> nonPrimitiveArgs)373     boolean hasNonConstArg(JFunc jfunc, CFunc cfunc, List<Integer> nonPrimitiveArgs) {
374         if (nonPrimitiveArgs.size() > 0) {
375             for (int i = nonPrimitiveArgs.size() - 1; i >= 0; i--) {
376                 int idx = nonPrimitiveArgs.get(i).intValue();
377                 int cIndex = jfunc.getArgCIndex(idx);
378                 if (jfunc.getArgType(idx).isArray()) {
379                     if (!cfunc.getArgType(cIndex).isConst()) {
380                         return true;
381                     }
382                 } else if (jfunc.getArgType(idx).isBuffer()) {
383                     if (!cfunc.getArgType(cIndex).isConst()) {
384                         return true;
385                     }
386                 }
387             }
388         }
389 
390         return false;
391     }
392 
393     /**
394      * Emit a function in several variants:
395      *
396      * if nativeDecl: public native <returntype> func(args);
397      *
398      * if !nativeDecl:
399      *   if interfaceDecl:  public <returntype> func(args);
400      *   if !interfaceDecl: public <returntype> func(args) { body }
401      */
emitFunction(JFunc jfunc, PrintStream out, boolean nativeDecl, boolean interfaceDecl)402     void emitFunction(JFunc jfunc, PrintStream out, boolean nativeDecl, boolean interfaceDecl) {
403         boolean isPointerFunc =
404             jfunc.getName().endsWith("Pointer") &&
405             jfunc.getCFunc().hasPointerArg();
406 
407         if (!nativeDecl && !interfaceDecl && !isPointerFunc) {
408             // If it's not a pointer function, we've already emitted it
409             // with nativeDecl == true
410             return;
411         }
412 
413         String maybeStatic = mUseStaticMethods ? "static " : "";
414 
415         if (isPointerFunc) {
416             out.println(indent +
417                         (nativeDecl ? "private " + maybeStatic +"native " :
418                          (interfaceDecl ? "" : "public ") + maybeStatic) +
419                         jfunc.getType() + " " +
420                         jfunc.getName() +
421                         (nativeDecl ? "Bounds" : "") +
422                         "(");
423         } else {
424             out.println(indent +
425                         (nativeDecl ? "public " + maybeStatic +"native " :
426                          (interfaceDecl ? "" : "public ") + maybeStatic) +
427                         jfunc.getType() + " " +
428                         jfunc.getName() +
429                         "(");
430         }
431 
432         int numArgs = jfunc.getNumArgs();
433         for (int i = 0; i < numArgs; i++) {
434             String argName = jfunc.getArgName(i);
435             JType argType = jfunc.getArgType(i);
436 
437             out.print(indent + indent + argType + " " + argName);
438             if (i == numArgs - 1) {
439                 if (isPointerFunc && nativeDecl) {
440                     out.println(",");
441                     out.println(indent + indent + "int remaining");
442                 } else {
443                     out.println();
444                 }
445             } else {
446                 out.println(",");
447             }
448         }
449 
450         if (nativeDecl || interfaceDecl) {
451             out.println(indent + ");");
452         } else {
453             out.println(indent + ") {");
454 
455             String iii = indent + indent;
456 
457             // emitBoundsChecks(jfunc, out, iii);
458             emitFunctionCall(jfunc, out, iii, false);
459 
460             // Set the pointer after we call the native code, so that if
461             // the native code throws an exception we don't modify the
462             // pointer. We assume that the native code is written so that
463             // if an exception is thrown, then the underlying glXXXPointer
464             // function will not have been called.
465 
466             String fname = jfunc.getName();
467             if (isPointerFunc) {
468                 // TODO - deal with VBO variants
469                 if (fname.equals("glColorPointer")) {
470                     out.println(iii + "if ((size == 4) &&");
471                     out.println(iii + "    ((type == GL_FLOAT) ||");
472                     out.println(iii + "     (type == GL_UNSIGNED_BYTE) ||");
473                     out.println(iii + "     (type == GL_FIXED)) &&");
474                     out.println(iii + "    (stride >= 0)) {");
475                     out.println(iii + indent + "_colorPointer = pointer;");
476                     out.println(iii + "}");
477                 } else if (fname.equals("glNormalPointer")) {
478                     out.println(iii + "if (((type == GL_FLOAT) ||");
479                     out.println(iii + "     (type == GL_BYTE) ||");
480                     out.println(iii + "     (type == GL_SHORT) ||");
481                     out.println(iii + "     (type == GL_FIXED)) &&");
482                     out.println(iii + "    (stride >= 0)) {");
483                     out.println(iii + indent + "_normalPointer = pointer;");
484                     out.println(iii + "}");
485                 } else if (fname.equals("glTexCoordPointer")) {
486                     out.println(iii + "if (((size == 2) ||");
487                     out.println(iii + "     (size == 3) ||");
488                     out.println(iii + "     (size == 4)) &&");
489                     out.println(iii + "    ((type == GL_FLOAT) ||");
490                     out.println(iii + "     (type == GL_BYTE) ||");
491                     out.println(iii + "     (type == GL_SHORT) ||");
492                     out.println(iii + "     (type == GL_FIXED)) &&");
493                     out.println(iii + "    (stride >= 0)) {");
494                     out.println(iii + indent + "_texCoordPointer = pointer;");
495                     out.println(iii + "}");
496                 } else if (fname.equals("glVertexPointer")) {
497                     out.println(iii + "if (((size == 2) ||");
498                     out.println(iii + "     (size == 3) ||");
499                     out.println(iii + "     (size == 4)) &&");
500                     out.println(iii + "    ((type == GL_FLOAT) ||");
501                     out.println(iii + "     (type == GL_BYTE) ||");
502                     out.println(iii + "     (type == GL_SHORT) ||");
503                     out.println(iii + "     (type == GL_FIXED)) &&");
504                     out.println(iii + "    (stride >= 0)) {");
505                     out.println(iii + indent + "_vertexPointer = pointer;");
506                     out.println(iii + "}");
507                 }
508             }
509 
510             boolean isVoid = jfunc.getType().isVoid();
511 
512             if (!isVoid) {
513                 out.println(indent + indent + "return _returnValue;");
514             }
515             out.println(indent + "}");
516         }
517         out.println();
518     }
519 
addNativeRegistration(String s)520     public void addNativeRegistration(String s) {
521         nativeRegistrations.add(s);
522     }
523 
emitNativeRegistration(String registrationFunctionName, PrintStream cStream)524     public void emitNativeRegistration(String registrationFunctionName,
525             PrintStream cStream) {
526         cStream.println("static const char *classPathName = \"" +
527                         mClassPathName +
528                         "\";");
529         cStream.println();
530 
531         cStream.println("static JNINativeMethod methods[] = {");
532 
533         cStream.println("{\"_nativeClassInit\", \"()V\", (void*)nativeClassInit },");
534 
535         Iterator<String> i = nativeRegistrations.iterator();
536         while (i.hasNext()) {
537             cStream.println(i.next());
538         }
539 
540         cStream.println("};");
541         cStream.println();
542 
543 
544         cStream.println("int " + registrationFunctionName + "(JNIEnv *_env)");
545         cStream.println("{");
546         cStream.println(indent +
547                         "int err;");
548 
549         cStream.println(indent +
550                         "err = android::AndroidRuntime::registerNativeMethods(_env, classPathName, methods, NELEM(methods));");
551 
552         cStream.println(indent + "return err;");
553         cStream.println("}");
554     }
555 
JniCodeEmitter()556     public JniCodeEmitter() {
557         super();
558     }
559 
getJniType(JType jType)560     String getJniType(JType jType) {
561         if (jType.isVoid()) {
562             return "void";
563         }
564 
565         String baseType = jType.getBaseType();
566         if (jType.isPrimitive()) {
567             if (baseType.equals("String")) {
568                 return "jstring";
569             } else {
570                 return "j" + baseType;
571             }
572         } else if (jType.isArray()) {
573             return "j" + baseType + "Array";
574         } else {
575             return "jobject";
576         }
577     }
578 
getJniMangledName(String name)579     String getJniMangledName(String name) {
580         name = name.replaceAll("_", "_1");
581         name = name.replaceAll(";", "_2");
582         name = name.replaceAll("\\[", "_3");
583         return name;
584     }
585 
emitJniCode(JFunc jfunc, PrintStream out)586     public void emitJniCode(JFunc jfunc, PrintStream out) {
587         CFunc cfunc = jfunc.getCFunc();
588 
589         // Emit comment identifying original C function
590         //
591         // Example:
592         //
593         // /* void glClipPlanef ( GLenum plane, const GLfloat *equation ) */
594         //
595         out.println("/* " + cfunc.getOriginal() + " */");
596 
597         // Emit JNI signature (name)
598         //
599         // Example:
600         //
601         // void
602         // android_glClipPlanef__I_3FI
603         //
604 
605         String outName = "android_" + jfunc.getName();
606         boolean isPointerFunc = outName.endsWith("Pointer") &&
607             jfunc.getCFunc().hasPointerArg();
608         boolean isVBOPointerFunc = (outName.endsWith("Pointer") ||
609             outName.endsWith("DrawElements")) &&
610             !jfunc.getCFunc().hasPointerArg();
611         if (isPointerFunc) {
612             outName += "Bounds";
613         }
614 
615         out.print("static ");
616         out.println(getJniType(jfunc.getType()));
617         out.print(outName);
618 
619         String rsignature = getJniName(jfunc.getType());
620 
621         String signature = "";
622         int numArgs = jfunc.getNumArgs();
623         for (int i = 0; i < numArgs; i++) {
624             JType argType = jfunc.getArgType(i);
625             signature += getJniName(argType);
626         }
627         if (isPointerFunc) {
628             signature += "I";
629         }
630 
631         // Append signature to function name
632         String sig = getJniMangledName(signature).replace('.', '_');
633         out.print("__" + sig);
634         outName += "__" + sig;
635 
636         signature = signature.replace('.', '/');
637         rsignature = rsignature.replace('.', '/');
638 
639         out.println();
640         if (rsignature.length() == 0) {
641             rsignature = "V";
642         }
643 
644         String s = "{\"" +
645             jfunc.getName() +
646             (isPointerFunc ? "Bounds" : "") +
647             "\", \"(" + signature +")" +
648             rsignature +
649             "\", (void *) " +
650             outName +
651             " },";
652         nativeRegistrations.add(s);
653 
654         List<Integer> nonPrimitiveArgs = new ArrayList<Integer>();
655         int numBufferArgs = 0;
656         List<String> bufferArgNames = new ArrayList<String>();
657 
658         // Emit JNI signature (arguments)
659         //
660         // Example:
661         //
662         // (JNIEnv *_env, jobject this, jint plane, jfloatArray equation_ref, jint offset) {
663         //
664         out.print("  (JNIEnv *_env, jobject _this");
665         for (int i = 0; i < numArgs; i++) {
666             out.print(", ");
667             JType argType = jfunc.getArgType(i);
668             String suffix;
669             if (!argType.isPrimitive()) {
670                 if (argType.isArray()) {
671                     suffix = "_ref";
672                 } else {
673                     suffix = "_buf";
674                 }
675                 nonPrimitiveArgs.add(new Integer(i));
676                 if (jfunc.getArgType(i).isBuffer()) {
677                     int cIndex = jfunc.getArgCIndex(i);
678                     String cname = cfunc.getArgName(cIndex);
679                     bufferArgNames.add(cname);
680                     numBufferArgs++;
681                 }
682             } else {
683                 suffix = "";
684             }
685 
686             out.print(getJniType(argType) + " " + jfunc.getArgName(i) + suffix);
687         }
688         if (isPointerFunc) {
689             out.print(", jint remaining");
690         }
691         out.println(") {");
692 
693         int numArrays = 0;
694         int numBuffers = 0;
695         for (int i = 0; i < nonPrimitiveArgs.size(); i++) {
696             int idx = nonPrimitiveArgs.get(i).intValue();
697             if (jfunc.getArgType(idx).isArray()) {
698                 ++numArrays;
699             }
700             if (jfunc.getArgType(idx).isBuffer()) {
701                 ++numBuffers;
702             }
703         }
704 
705         // Emit method body
706 
707         // Emit local variable declarations for _exception and _returnValue
708         //
709         // Example:
710         //
711         // android::gl::ogles_context_t *ctx;
712         //
713         // jint _exception;
714         // GLenum _returnValue;
715         //
716         CType returnType = cfunc.getType();
717         boolean isVoid = returnType.isVoid();
718 
719         boolean isUnsupported = isUnsupportedFunc(cfunc);
720         if (isUnsupported) {
721             out.println(indent +
722                         "_env->ThrowNew(UOEClass,");
723             out.println(indent +
724                         "    \"" + cfunc.getName() + "\");");
725             if (!isVoid) {
726                 String retval = getErrorReturnValue(cfunc);
727                 out.println(indent + "return " + retval + ";");
728             }
729             out.println("}");
730             out.println();
731             return;
732         }
733 
734         if (mUseContextPointer) {
735             out.println(indent +
736                 "android::gl::ogles_context_t *ctx = getContext(_env, _this);");
737         }
738 
739         boolean emitExceptionCheck = (numArrays > 0 || numBuffers > 0) &&
740             hasNonConstArg(jfunc, cfunc, nonPrimitiveArgs);
741         // mChecker.getChecks(cfunc.getName()) != null
742 
743         // Emit an _exeption variable if there will be error checks
744         if (emitExceptionCheck) {
745             out.println(indent + "jint _exception = 0;");
746         }
747 
748         // Emit a single _array or multiple _XXXArray variables
749         if (numBufferArgs == 1) {
750                 out.println(indent + "jarray _array = (jarray) 0;");
751         } else {
752             for (int i = 0; i < numBufferArgs; i++) {
753                 out.println(indent + "jarray _" + bufferArgNames.get(i) +
754                             "Array = (jarray) 0;");
755             }
756         }
757         if (!isVoid) {
758             String retval = getErrorReturnValue(cfunc);
759             if (retval != null) {
760                 out.println(indent + returnType.getDeclaration() +
761                             " _returnValue = " + retval + ";");
762             } else {
763                 out.println(indent + returnType.getDeclaration() +
764                             " _returnValue;");
765             }
766         }
767 
768         // Emit local variable declarations for pointer arguments
769         //
770         // Example:
771         //
772         // GLfixed *eqn_base;
773         // GLfixed *eqn;
774         //
775         String offset = "offset";
776         String remaining = "_remaining";
777         if (nonPrimitiveArgs.size() > 0) {
778             for (int i = 0; i < nonPrimitiveArgs.size(); i++) {
779                 int idx = nonPrimitiveArgs.get(i).intValue();
780                 int cIndex = jfunc.getArgCIndex(idx);
781                 String cname = cfunc.getArgName(cIndex);
782 
783                 CType type = cfunc.getArgType(jfunc.getArgCIndex(idx));
784                 String decl = type.getDeclaration();
785                 if (jfunc.getArgType(idx).isArray()) {
786                     out.println(indent +
787                                 decl +
788                                 (decl.endsWith("*") ? "" : " ") +
789                                 jfunc.getArgName(idx) +
790                                 "_base = (" + decl + ") 0;");
791                 }
792                 remaining = (numArrays <= 1 && numBuffers <= 1) ? "_remaining" :
793                     "_" + cname + "Remaining";
794                 out.println(indent +
795                             "jint " + remaining + ";");
796                 out.println(indent +
797                             decl +
798                             (decl.endsWith("*") ? "" : " ") +
799                             jfunc.getArgName(idx) +
800                             " = (" + decl + ") 0;");
801             }
802 
803             out.println();
804         }
805 
806         // Emit 'GetPrimitiveArrayCritical' for arrays
807         // Emit 'GetPointer' calls for Buffer pointers
808         int bufArgIdx = 0;
809         if (nonPrimitiveArgs.size() > 0) {
810             for (int i = 0; i < nonPrimitiveArgs.size(); i++) {
811                 int idx = nonPrimitiveArgs.get(i).intValue();
812                 int cIndex = jfunc.getArgCIndex(idx);
813 
814                 String cname = cfunc.getArgName(cIndex);
815                 offset = numArrays <= 1 ? "offset" :
816                     cname + "Offset";
817                 remaining = (numArrays <= 1 && numBuffers <= 1) ? "_remaining" :
818                     "_" + cname + "Remaining";
819 
820                 if (jfunc.getArgType(idx).isArray()) {
821                     out.println(indent +
822                                 "if (!" +
823                                 cname +
824                                 "_ref) {");
825                     if (emitExceptionCheck) {
826                         out.println(indent + indent + "_exception = 1;");
827                     }
828                     out.println(indent + "    " +
829                                 (mUseCPlusPlus ? "_env" : "(*_env)") +
830                                 "->ThrowNew(" +
831                                 (mUseCPlusPlus ? "" : "_env, ") +
832                                 "IAEClass, " +
833                                 "\"" + cname +
834                                 " == null\");");
835                     out.println(indent + "    goto exit;");
836                     needsExit = true;
837                     out.println(indent + "}");
838 
839                     out.println(indent + "if (" + offset + " < 0) {");
840                     if (emitExceptionCheck) {
841                         out.println(indent + indent + "_exception = 1;");
842                     }
843                     out.println(indent + "    " +
844                                 (mUseCPlusPlus ? "_env" : "(*_env)") +
845                                 "->ThrowNew(" +
846                                 (mUseCPlusPlus ? "" : "_env, ") +
847                                 "IAEClass, " +
848                                 "\"" + offset + " < 0\");");
849                     out.println(indent + "    goto exit;");
850                     needsExit = true;
851                     out.println(indent + "}");
852 
853                     out.println(indent + remaining + " = " +
854                                     (mUseCPlusPlus ? "_env" : "(*_env)") +
855                                     "->GetArrayLength(" +
856                                     (mUseCPlusPlus ? "" : "_env, ") +
857                                     cname + "_ref) - " + offset + ";");
858 
859                     emitNativeBoundsChecks(cfunc, cname, out, false,
860                                            emitExceptionCheck,
861                                            offset, remaining, "    ");
862 
863                     out.println(indent +
864                                 cname +
865                                 "_base = (" +
866                                 cfunc.getArgType(cIndex).getDeclaration() +
867                                 ")");
868                     out.println(indent + "    " +
869                                 (mUseCPlusPlus ? "_env" : "(*_env)") +
870                                 "->GetPrimitiveArrayCritical(" +
871                                 (mUseCPlusPlus ? "" : "_env, ") +
872                                 jfunc.getArgName(idx) +
873                                 "_ref, (jboolean *)0);");
874                     out.println(indent +
875                                 cname + " = " + cname + "_base + " + offset +
876                                 ";");
877                     out.println();
878                 } else {
879                     String array = numBufferArgs <= 1 ? "_array" :
880                         "_" + bufferArgNames.get(bufArgIdx++) + "Array";
881 
882                     boolean nullAllowed = isNullAllowed(cfunc) || isPointerFunc;
883                     if (nullAllowed) {
884                         out.println(indent + "if (" + cname + "_buf) {");
885                         out.print(indent);
886                     }
887 
888                     if (isPointerFunc) {
889                         out.println(indent +
890                                 cname +
891                                 " = (" +
892                                 cfunc.getArgType(cIndex).getDeclaration() +
893                                 ") getDirectBufferPointer(_env, " +
894                                 cname + "_buf);");
895                         String iii = "    ";
896                         out.println(iii + indent + "if ( ! " + cname + " ) {");
897                         out.println(iii + iii + indent + "return;");
898                         out.println(iii + indent + "}");
899                     } else {
900                         out.println(indent +
901                                     cname +
902                                     " = (" +
903                                     cfunc.getArgType(cIndex).getDeclaration() +
904                                     ")getPointer(_env, " +
905                                     cname +
906                                     "_buf, &" + array + ", &" + remaining +
907                                     ");");
908                     }
909 
910                     emitNativeBoundsChecks(cfunc, cname, out, true,
911                                            emitExceptionCheck,
912                                            offset, remaining, nullAllowed ? "        " : "    ");
913 
914                     if (nullAllowed) {
915                         out.println(indent + "}");
916                     }
917                 }
918             }
919         }
920 
921         if (!isVoid) {
922             out.print(indent + "_returnValue = ");
923         } else {
924             out.print(indent);
925         }
926         String name = cfunc.getName();
927 
928         if (mUseContextPointer) {
929             name = name.substring(2, name.length()); // Strip off 'gl' prefix
930             name = name.substring(0, 1).toLowerCase() +
931                 name.substring(1, name.length());
932             out.print("ctx->procs.");
933         }
934 
935         out.print(name + (isPointerFunc ? "Bounds" : "") + "(");
936 
937         numArgs = cfunc.getNumArgs();
938         if (numArgs == 0) {
939             if (mUseContextPointer) {
940                 out.println("ctx);");
941             } else {
942                 out.println(");");
943             }
944         } else {
945             if (mUseContextPointer) {
946                 out.println("ctx,");
947             } else {
948                 out.println();
949             }
950             for (int i = 0; i < numArgs; i++) {
951                 String typecast;
952                 if (i == numArgs - 1 && isVBOPointerFunc) {
953                     typecast = "const GLvoid *";
954                 } else {
955                     typecast = cfunc.getArgType(i).getDeclaration();
956                 }
957                 out.print(indent + indent +
958                           "(" +
959                           typecast +
960                           ")" +
961                           cfunc.getArgName(i));
962 
963                 if (i == numArgs - 1) {
964                     if (isPointerFunc) {
965                         out.println(",");
966                         out.println(indent + indent + "(GLsizei)remaining");
967                     } else {
968                         out.println();
969                     }
970                 } else {
971                     out.println(",");
972                 }
973             }
974             out.println(indent + ");");
975         }
976 
977         if (needsExit) {
978             out.println();
979             out.println("exit:");
980             needsExit = false;
981         }
982 
983         bufArgIdx = 0;
984         if (nonPrimitiveArgs.size() > 0) {
985             for (int i = nonPrimitiveArgs.size() - 1; i >= 0; i--) {
986                 int idx = nonPrimitiveArgs.get(i).intValue();
987 
988                 int cIndex = jfunc.getArgCIndex(idx);
989                 if (jfunc.getArgType(idx).isArray()) {
990 
991                     // If the argument is 'const', GL will not write to it.
992                     // In this case, we can use the 'JNI_ABORT' flag to avoid
993                     // the need to write back to the Java array
994                     out.println(indent +
995                                 "if (" + jfunc.getArgName(idx) + "_base) {");
996                     out.println(indent + indent +
997                                 (mUseCPlusPlus ? "_env" : "(*_env)") +
998                                 "->ReleasePrimitiveArrayCritical(" +
999                                 (mUseCPlusPlus ? "" : "_env, ") +
1000                                 jfunc.getArgName(idx) + "_ref, " +
1001                                 cfunc.getArgName(cIndex) +
1002                                 "_base,");
1003                     out.println(indent + indent + indent +
1004                                 (cfunc.getArgType(cIndex).isConst() ?
1005                                  "JNI_ABORT" :
1006                                  "_exception ? JNI_ABORT: 0") +
1007                                 ");");
1008                     out.println(indent + "}");
1009                 } else if (jfunc.getArgType(idx).isBuffer()) {
1010                     if (! isPointerFunc) {
1011                         String array = numBufferArgs <= 1 ? "_array" :
1012                             "_" + bufferArgNames.get(bufArgIdx++) + "Array";
1013                         out.println(indent + "if (" + array + ") {");
1014                         out.println(indent + indent +
1015                                     "releasePointer(_env, " + array + ", " +
1016                                     cfunc.getArgName(cIndex) +
1017                                     ", " +
1018                                     (cfunc.getArgType(cIndex).isConst() ?
1019                                      "JNI_FALSE" : "_exception ? JNI_FALSE :" +
1020                                              " JNI_TRUE") +
1021                                     ");");
1022                         out.println(indent + "}");
1023                     }
1024                 }
1025             }
1026         }
1027 
1028         if (!isVoid) {
1029             out.println(indent + "return _returnValue;");
1030         }
1031 
1032         out.println("}");
1033         out.println();
1034     }
1035 
1036 }
1037