1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 import java.io.PrintStream;
18 import java.util.ArrayList;
19 import java.util.HashSet;
20 import java.util.Iterator;
21 import java.util.List;
22 
23 public class JniCodeEmitter {
24 
25     static final boolean mUseCPlusPlus = true;
26     protected boolean mUseContextPointer = true;
27     protected boolean mUseStaticMethods = false;
28     protected boolean mUseSimpleMethodNames = false;
29     protected boolean mUseHideCommentForAPI = false;
30     protected String mClassPathName;
31     protected ParameterChecker mChecker;
32     protected List<String> nativeRegistrations = new ArrayList<String>();
33     boolean needsExit;
34     protected static String indent = "    ";
35     HashSet<String> mFunctionsEmitted = new HashSet<String>();
36 
getJniName(JType jType)37     public static String getJniName(JType jType) {
38         String jniName = "";
39         if (jType.isEGLHandle()) {
40             return (jType.isArray() ? "[" : "" ) + "Landroid/opengl/" + jType.getBaseType() + ";";
41         } else if (jType.isClass()) {
42             return "L" + jType.getBaseType() + ";";
43         } else if (jType.isArray()) {
44             jniName = "[";
45         }
46 
47         String baseType = jType.getBaseType();
48         if (baseType.equals("int")) {
49             jniName += "I";
50         } else if (baseType.equals("float")) {
51             jniName += "F";
52         } else if (baseType.equals("boolean")) {
53             jniName += "Z";
54         } else if (baseType.equals("short")) {
55             jniName += "S";
56         } else if (baseType.equals("long")) {
57             jniName += "J";
58         } else if (baseType.equals("byte")) {
59             jniName += "B";
60         } else if (baseType.equals("String")) {
61             jniName += "Ljava/lang/String;";
62         } else if (baseType.equals("void")) {
63             // nothing.
64         } else {
65             throw new RuntimeException("Unknown primitive basetype " + baseType);
66         }
67         return jniName;
68     }
69 
emitCode(CFunc cfunc, String original, PrintStream javaInterfaceStream, PrintStream javaImplStream, PrintStream cStream)70     public void emitCode(CFunc cfunc, String original,
71             PrintStream javaInterfaceStream,
72             PrintStream javaImplStream,
73             PrintStream cStream) {
74         JFunc jfunc;
75         String signature;
76         boolean duplicate;
77 
78         if (cfunc.hasTypedPointerArg()) {
79             jfunc = JFunc.convert(cfunc, true);
80 
81             // Don't emit duplicate functions
82             // These may appear because they are defined in multiple
83             // Java interfaces (e.g., GL11/GL11ExtensionPack)
84             signature = jfunc.toString();
85             duplicate = false;
86             if (mFunctionsEmitted.contains(signature)) {
87                 duplicate = true;
88             } else {
89                 mFunctionsEmitted.add(signature);
90             }
91 
92             if (!duplicate) {
93                 emitNativeDeclaration(jfunc, javaImplStream);
94                 emitJavaCode(jfunc, javaImplStream);
95             }
96             if (javaInterfaceStream != null) {
97                 emitJavaInterfaceCode(jfunc, javaInterfaceStream);
98             }
99             if (!duplicate) {
100                 emitJniCode(jfunc, cStream);
101             }
102             // Don't create IOBuffer versions of the EGL functions
103             if (cfunc.hasEGLHandleArg()) {
104                 return;
105             }
106         }
107 
108         jfunc = JFunc.convert(cfunc, false);
109 
110         signature = jfunc.toString();
111         duplicate = false;
112         if (mFunctionsEmitted.contains(signature)) {
113             duplicate = true;
114         } else {
115             mFunctionsEmitted.add(signature);
116         }
117 
118         if (!duplicate) {
119             emitNativeDeclaration(jfunc, javaImplStream);
120         }
121         if (javaInterfaceStream != null) {
122             emitJavaInterfaceCode(jfunc, javaInterfaceStream);
123         }
124         if (!duplicate) {
125             emitJavaCode(jfunc, javaImplStream);
126             emitJniCode(jfunc, cStream);
127         }
128     }
129 
emitNativeDeclaration(JFunc jfunc, PrintStream out)130     public void emitNativeDeclaration(JFunc jfunc, PrintStream out) {
131         if (mUseHideCommentForAPI) {
132             out.println("    /* @hide C function " + jfunc.getCFunc().getOriginal() + " */");
133             out.println();
134         } else {
135             out.println("    // C function " + jfunc.getCFunc().getOriginal());
136             out.println();
137         }
138 
139         emitFunction(jfunc, out, true, false);
140     }
141 
emitJavaInterfaceCode(JFunc jfunc, PrintStream out)142     public void emitJavaInterfaceCode(JFunc jfunc, PrintStream out) {
143         emitFunction(jfunc, out, false, true);
144     }
145 
emitJavaCode(JFunc jfunc, PrintStream out)146     public void emitJavaCode(JFunc jfunc, PrintStream out) {
147         emitFunction(jfunc, out, false, false);
148     }
149 
isPointerFunc(JFunc jfunc)150     boolean isPointerFunc(JFunc jfunc) {
151         String name = jfunc.getName();
152         return (name.endsWith("Pointer") || name.endsWith("PointerOES"))
153             && jfunc.getCFunc().hasPointerArg();
154     }
155 
emitFunctionCall(JFunc jfunc, PrintStream out, String iii, boolean grabArray)156     void emitFunctionCall(JFunc jfunc, PrintStream out, String iii, boolean grabArray) {
157         boolean isVoid = jfunc.getType().isVoid();
158         boolean isPointerFunc = isPointerFunc(jfunc);
159 
160         if (!isVoid) {
161             out.println(iii +
162                         jfunc.getType() + " _returnValue;");
163         }
164         out.println(iii +
165                     (isVoid ? "" : "_returnValue = ") +
166                     jfunc.getName() +
167                     (isPointerFunc ? "Bounds" : "" ) +
168                     "(");
169 
170         int numArgs = jfunc.getNumArgs();
171         for (int i = 0; i < numArgs; i++) {
172             String argName = jfunc.getArgName(i);
173             JType argType = jfunc.getArgType(i);
174 
175             if (grabArray && argType.isTypedBuffer()) {
176                 String typeName = argType.getBaseType();
177                 typeName = typeName.substring(9, typeName.length() - 6);
178                 out.println(iii + indent + "get" + typeName + "Array(" + argName + "),");
179                 out.print(iii + indent + "getOffset(" + argName + ")");
180             } else {
181                 out.print(iii + indent + argName);
182             }
183             if (i == numArgs - 1) {
184                 if (isPointerFunc) {
185                     out.println(",");
186                     out.println(iii + indent + argName + ".remaining()");
187                 } else {
188                     out.println();
189                 }
190             } else {
191                 out.println(",");
192             }
193         }
194 
195         out.println(iii + ");");
196     }
197 
printIfcheckPostamble(PrintStream out, boolean isBuffer, boolean emitExceptionCheck, String iii)198     void printIfcheckPostamble(PrintStream out, boolean isBuffer, boolean emitExceptionCheck,
199             String iii) {
200         printIfcheckPostamble(out, isBuffer, emitExceptionCheck,
201                 "offset", "_remaining", iii);
202     }
203 
printIfcheckPostamble(PrintStream out, boolean isBuffer, boolean emitExceptionCheck, String offset, String remaining, String iii)204     void printIfcheckPostamble(PrintStream out, boolean isBuffer, boolean emitExceptionCheck,
205             String offset, String remaining, String iii) {
206         out.println(iii + "    default:");
207         out.println(iii + "        _needed = 1;");
208         out.println(iii + "        break;");
209         out.println(iii + "}");
210 
211         out.println(iii + "if (" + remaining + " < _needed) {");
212         out.println(iii + indent + "_exception = 1;");
213         out.println(iii + indent +
214                 "_exceptionType = \"java/lang/IllegalArgumentException\";");
215         out.println(iii + indent +
216                 "_exceptionMessage = \"" +
217                 (isBuffer ? "remaining()" : "length - " + offset) +
218                 " < needed\";");
219         out.println(iii + indent + "goto exit;");
220         out.println(iii + "}");
221 
222         needsExit = true;
223     }
224 
isNullAllowed(CFunc cfunc)225     boolean isNullAllowed(CFunc cfunc) {
226         String[] checks = mChecker.getChecks(cfunc.getName());
227         int index = 1;
228         if (checks != null) {
229             while (index < checks.length) {
230                 if (checks[index].equals("nullAllowed")) {
231                     return true;
232                 } else {
233                     index = skipOneCheck(checks, index);
234                 }
235             }
236         }
237         return false;
238     }
239 
hasCheckTest(CFunc cfunc)240     boolean hasCheckTest(CFunc cfunc) {
241         String[] checks = mChecker.getChecks(cfunc.getName());
242         int index = 1;
243         if (checks != null) {
244             while (index < checks.length) {
245                 if (checks[index].startsWith("check")) {
246                     return true;
247                 } else {
248                     index = skipOneCheck(checks, index);
249                 }
250             }
251         }
252         return false;
253     }
254 
hasIfTest(CFunc cfunc)255     boolean hasIfTest(CFunc cfunc) {
256         String[] checks = mChecker.getChecks(cfunc.getName());
257         int index = 1;
258         if (checks != null) {
259             while (index < checks.length) {
260                 if (checks[index].startsWith("ifcheck")) {
261                     return true;
262                 } else {
263                     index = skipOneCheck(checks, index);
264                 }
265             }
266         }
267         return false;
268     }
269 
skipOneCheck(String[] checks, int index)270     int skipOneCheck(String[] checks, int index) {
271         if (checks[index].equals("return")) {
272             index += 2;
273         } else if (checks[index].startsWith("check")) {
274             index += 3;
275         } else if (checks[index].startsWith("sentinel")) {
276             index += 3;
277         } else if (checks[index].equals("ifcheck")) {
278             index += 5;
279         } else if (checks[index].equals("unsupported")) {
280             index += 1;
281         } else if (checks[index].equals("requires")) {
282             index += 2;
283         } else if (checks[index].equals("nullAllowed")) {
284             index += 1;
285         } else {
286             System.out.println("Error: unknown keyword \"" +
287                                checks[index] + "\"");
288             System.exit(0);
289         }
290 
291         return index;
292     }
293 
getErrorReturnValue(CFunc cfunc)294     String getErrorReturnValue(CFunc cfunc) {
295         CType returnType = cfunc.getType();
296         boolean isVoid = returnType.isVoid();
297         if (isVoid) {
298             return null;
299         }
300 
301         if (returnType.getBaseType().startsWith("EGL")) {
302             return "(" + returnType.getDeclaration() + ") 0";
303         }
304 
305         String[] checks = mChecker.getChecks(cfunc.getName());
306 
307         int index = 1;
308         if (checks != null) {
309             while (index < checks.length) {
310                 if (checks[index].equals("return")) {
311                     return checks[index + 1];
312                 } else {
313                     index = skipOneCheck(checks, index);
314                 }
315             }
316         }
317 
318         return null;
319     }
320 
isUnsupportedFunc(CFunc cfunc)321     boolean isUnsupportedFunc(CFunc cfunc) {
322         String[] checks = mChecker.getChecks(cfunc.getName());
323         int index = 1;
324         if (checks != null) {
325             while (index < checks.length) {
326                 if (checks[index].equals("unsupported")) {
327                     return true;
328                 } else {
329                     index = skipOneCheck(checks, index);
330                 }
331             }
332         }
333         return false;
334     }
335 
isRequiresFunc(CFunc cfunc)336     String isRequiresFunc(CFunc cfunc) {
337         String[] checks = mChecker.getChecks(cfunc.getName());
338         int index = 1;
339         if (checks != null) {
340             while (index < checks.length) {
341                 if (checks[index].equals("requires")) {
342                     return checks[index+1];
343                 } else {
344                     index = skipOneCheck(checks, index);
345                 }
346             }
347         }
348         return null;
349     }
350 
emitNativeBoundsChecks(CFunc cfunc, String cname, PrintStream out, boolean isBuffer, boolean emitExceptionCheck, String offset, String remaining, String iii)351     void emitNativeBoundsChecks(CFunc cfunc, String cname, PrintStream out,
352             boolean isBuffer, boolean emitExceptionCheck, String offset, String remaining, String iii) {
353 
354         String[] checks = mChecker.getChecks(cfunc.getName());
355 
356         boolean lastWasIfcheck = false;
357 
358         int index = 1;
359         if (checks != null) {
360             while (index < checks.length) {
361                 if (checks[index].startsWith("check")) {
362                     if (lastWasIfcheck) {
363                         printIfcheckPostamble(out, isBuffer, emitExceptionCheck,
364                                 offset, remaining, iii);
365                     }
366                     lastWasIfcheck = false;
367                     if (cname != null && !cname.equals(checks[index + 1])) {
368                         index += 3;
369                         continue;
370                     }
371                     out.println(iii + "if (" + remaining + " < " + checks[index + 2] + ") {");
372                     out.println(iii + indent + "_exception = 1;");
373                     String exceptionClassName = "java/lang/IllegalArgumentException";
374                     // If the "check" keyword was of the form
375                     // "check_<class name>", use the class name in the
376                     // exception to be thrown
377                     int underscore = checks[index].indexOf('_');
378                     if (underscore >= 0) {
379                         String abbr = checks[index].substring(underscore + 1);
380                         if (abbr.equals("AIOOBE")) {
381                             exceptionClassName = "java/lang/ArrayIndexOutOfBoundsException";
382                         } else {
383                             throw new RuntimeException("unknown exception abbreviation: " + abbr);
384                         }
385                     }
386                     out.println(iii + indent +
387                                 "_exceptionType = \""+exceptionClassName+"\";");
388                     out.println(iii + indent +
389                                "_exceptionMessage = \"" +
390                                (isBuffer ? "remaining()" : "length - " +
391                                offset) + " < " + checks[index + 2] +
392                                " < needed\";");
393 
394                     out.println(iii + indent + "goto exit;");
395                     out.println(iii + "}");
396 
397                     needsExit = true;
398 
399                     index += 3;
400                 } else if (checks[index].equals("ifcheck")) {
401                     String[] matches = checks[index + 4].split(",");
402 
403                     if (!lastWasIfcheck) {
404                         out.println(iii + "int _needed;");
405                         out.println(iii + "switch (" + checks[index + 3] + ") {");
406                     }
407 
408                     for (int i = 0; i < matches.length; i++) {
409                         out.println("#if defined(" + matches[i] + ")");
410                         out.println(iii + "    case " + matches[i] + ":");
411                         out.println("#endif // defined(" + matches[i] + ")");
412                     }
413                     out.println(iii + "        _needed = " + checks[index + 2] + ";");
414                     out.println(iii + "        break;");
415 
416                     lastWasIfcheck = true;
417                     index += 5;
418                 } else {
419                     index = skipOneCheck(checks, index);
420                 }
421             }
422         }
423 
424         if (lastWasIfcheck) {
425             printIfcheckPostamble(out, isBuffer, emitExceptionCheck, iii);
426         }
427     }
428 
emitSentinelCheck(CFunc cfunc, String cname, PrintStream out, boolean isBuffer, boolean emitExceptionCheck, String offset, String remaining, String iii)429     void emitSentinelCheck(CFunc cfunc, String cname, PrintStream out,
430             boolean isBuffer, boolean emitExceptionCheck, String offset, String remaining, String iii) {
431 
432         String[] checks = mChecker.getChecks(cfunc.getName());
433 
434         int index = 1;
435         if (checks != null) {
436             while (index < checks.length) {
437                 if (checks[index].startsWith("sentinel")) {
438                     if (cname != null && !cname.equals(checks[index + 1])) {
439                         index += 3;
440                         continue;
441                     }
442 
443                     out.println(iii + cname + "_sentinel = false;");
444                     out.println(iii + "for (int i = " + remaining +
445                                 " - 1; i >= 0; i--)  {");
446                     out.println(iii + indent + "if (" + cname +
447                                 "[i] == " + checks[index + 2] + "){");
448                     out.println(iii + indent + indent +
449                                 cname + "_sentinel = true;");
450                     out.println(iii + indent + indent + "break;");
451                     out.println(iii + indent + "}");
452                     out.println(iii + "}");
453                     out.println(iii +
454                                 "if (" + cname + "_sentinel == false) {");
455                     out.println(iii + indent + "_exception = 1;");
456                     out.println(iii + indent +
457                                 "_exceptionType = \"java/lang/IllegalArgumentException\";");
458                     out.println(iii + indent + "_exceptionMessage = \"" + cname +
459                                 " must contain " + checks[index + 2] + "!\";");
460                     out.println(iii + indent + "goto exit;");
461                     out.println(iii + "}");
462 
463                     needsExit = true;
464                     index += 3;
465                 } else {
466                     index = skipOneCheck(checks, index);
467                 }
468             }
469         }
470     }
471 
emitLocalVariablesForSentinel(CFunc cfunc, PrintStream out)472     void emitLocalVariablesForSentinel(CFunc cfunc, PrintStream out) {
473 
474         String[] checks = mChecker.getChecks(cfunc.getName());
475 
476         int index = 1;
477         if (checks != null) {
478             while (index < checks.length) {
479                 if (checks[index].startsWith("sentinel")) {
480                     String cname = checks[index + 1];
481                     out.println(indent + "bool " + cname + "_sentinel = false;");
482 
483                     index += 3;
484 
485                 } else {
486                     index = skipOneCheck(checks, index);
487                 }
488             }
489         }
490     }
491 
hasNonConstArg(JFunc jfunc, CFunc cfunc, List<Integer> nonPrimitiveArgs)492     boolean hasNonConstArg(JFunc jfunc, CFunc cfunc, List<Integer> nonPrimitiveArgs) {
493         if (nonPrimitiveArgs.size() > 0) {
494             for (int i = nonPrimitiveArgs.size() - 1; i >= 0; i--) {
495                 int idx = nonPrimitiveArgs.get(i).intValue();
496                 int cIndex = jfunc.getArgCIndex(idx);
497                 if (jfunc.getArgType(idx).isArray()) {
498                     if (!cfunc.getArgType(cIndex).isConst()) {
499                         return true;
500                     }
501                 } else if (jfunc.getArgType(idx).isBuffer()) {
502                     if (!cfunc.getArgType(cIndex).isConst()) {
503                         return true;
504                     }
505                 }
506             }
507         }
508 
509         return false;
510     }
511 
512     /**
513      * Emit a function in several variants:
514      *
515      * if nativeDecl: public native <returntype> func(args);
516      *
517      * if !nativeDecl:
518      *   if interfaceDecl:  public <returntype> func(args);
519      *   if !interfaceDecl: public <returntype> func(args) { body }
520      */
emitFunction(JFunc jfunc, PrintStream out, boolean nativeDecl, boolean interfaceDecl)521     void emitFunction(JFunc jfunc, PrintStream out, boolean nativeDecl, boolean interfaceDecl) {
522         boolean isPointerFunc = isPointerFunc(jfunc);
523 
524         if (!nativeDecl && !interfaceDecl && !isPointerFunc) {
525             // If it's not a pointer function, we've already emitted it
526             // with nativeDecl == true
527             return;
528         }
529 
530         String maybeStatic = mUseStaticMethods ? "static " : "";
531 
532         if (isPointerFunc) {
533             out.println(indent +
534                         (nativeDecl ? "private " + maybeStatic +"native " :
535                          (interfaceDecl ? "" : "public ") + maybeStatic) +
536                         jfunc.getType() + " " +
537                         jfunc.getName() +
538                         (nativeDecl ? "Bounds" : "") +
539                         "(");
540         } else {
541             out.println(indent +
542                         (nativeDecl ? "public " + maybeStatic +"native " :
543                          (interfaceDecl ? "" : "public ") + maybeStatic) +
544                         jfunc.getType() + " " +
545                         jfunc.getName() +
546                         "(");
547         }
548 
549         int numArgs = jfunc.getNumArgs();
550         for (int i = 0; i < numArgs; i++) {
551             String argName = jfunc.getArgName(i);
552             JType argType = jfunc.getArgType(i);
553 
554             out.print(indent + indent + argType + " " + argName);
555             if (i == numArgs - 1) {
556                 if (isPointerFunc && nativeDecl) {
557                     out.println(",");
558                     out.println(indent + indent + "int remaining");
559                 } else {
560                     out.println();
561                 }
562             } else {
563                 out.println(",");
564             }
565         }
566 
567         if (nativeDecl || interfaceDecl) {
568             out.println(indent + ");");
569         } else {
570             out.println(indent + ") {");
571 
572             String iii = indent + indent;
573 
574             // emitBoundsChecks(jfunc, out, iii);
575             emitFunctionCall(jfunc, out, iii, false);
576 
577             // Set the pointer after we call the native code, so that if
578             // the native code throws an exception we don't modify the
579             // pointer. We assume that the native code is written so that
580             // if an exception is thrown, then the underlying glXXXPointer
581             // function will not have been called.
582 
583             String fname = jfunc.getName();
584             if (isPointerFunc) {
585                 // TODO - deal with VBO variants
586                 if (fname.equals("glColorPointer")) {
587                     out.println(iii + "if ((size == 4) &&");
588                     out.println(iii + "    ((type == GL_FLOAT) ||");
589                     out.println(iii + "     (type == GL_UNSIGNED_BYTE) ||");
590                     out.println(iii + "     (type == GL_FIXED)) &&");
591                     out.println(iii + "    (stride >= 0)) {");
592                     out.println(iii + indent + "_colorPointer = pointer;");
593                     out.println(iii + "}");
594                 } else if (fname.equals("glNormalPointer")) {
595                     out.println(iii + "if (((type == GL_FLOAT) ||");
596                     out.println(iii + "     (type == GL_BYTE) ||");
597                     out.println(iii + "     (type == GL_SHORT) ||");
598                     out.println(iii + "     (type == GL_FIXED)) &&");
599                     out.println(iii + "    (stride >= 0)) {");
600                     out.println(iii + indent + "_normalPointer = pointer;");
601                     out.println(iii + "}");
602                 } else if (fname.equals("glTexCoordPointer")) {
603                     out.println(iii + "if (((size == 2) ||");
604                     out.println(iii + "     (size == 3) ||");
605                     out.println(iii + "     (size == 4)) &&");
606                     out.println(iii + "    ((type == GL_FLOAT) ||");
607                     out.println(iii + "     (type == GL_BYTE) ||");
608                     out.println(iii + "     (type == GL_SHORT) ||");
609                     out.println(iii + "     (type == GL_FIXED)) &&");
610                     out.println(iii + "    (stride >= 0)) {");
611                     out.println(iii + indent + "_texCoordPointer = pointer;");
612                     out.println(iii + "}");
613                 } else if (fname.equals("glVertexPointer")) {
614                     out.println(iii + "if (((size == 2) ||");
615                     out.println(iii + "     (size == 3) ||");
616                     out.println(iii + "     (size == 4)) &&");
617                     out.println(iii + "    ((type == GL_FLOAT) ||");
618                     out.println(iii + "     (type == GL_BYTE) ||");
619                     out.println(iii + "     (type == GL_SHORT) ||");
620                     out.println(iii + "     (type == GL_FIXED)) &&");
621                     out.println(iii + "    (stride >= 0)) {");
622                     out.println(iii + indent + "_vertexPointer = pointer;");
623                     out.println(iii + "}");
624                 } else if (fname.equals("glPointSizePointerOES")) {
625                     out.println(iii + "if (((type == GL_FLOAT) ||");
626                     out.println(iii + "     (type == GL_FIXED)) &&");
627                     out.println(iii + "    (stride >= 0)) {");
628                     out.println(iii + indent + "_pointSizePointerOES = pointer;");
629                     out.println(iii + "}");
630                 } else if (fname.equals("glMatrixIndexPointerOES")) {
631                     out.println(iii + "if (((size == 2) ||");
632                     out.println(iii + "     (size == 3) ||");
633                     out.println(iii + "     (size == 4)) &&");
634                     out.println(iii + "    ((type == GL_FLOAT) ||");
635                     out.println(iii + "     (type == GL_BYTE) ||");
636                     out.println(iii + "     (type == GL_SHORT) ||");
637                     out.println(iii + "     (type == GL_FIXED)) &&");
638                     out.println(iii + "    (stride >= 0)) {");
639                     out.println(iii + indent + "_matrixIndexPointerOES = pointer;");
640                     out.println(iii + "}");
641                 } else if (fname.equals("glWeightPointer")) {
642                     out.println(iii + "if (((size == 2) ||");
643                     out.println(iii + "     (size == 3) ||");
644                     out.println(iii + "     (size == 4)) &&");
645                     out.println(iii + "    ((type == GL_FLOAT) ||");
646                     out.println(iii + "     (type == GL_BYTE) ||");
647                     out.println(iii + "     (type == GL_SHORT) ||");
648                     out.println(iii + "     (type == GL_FIXED)) &&");
649                     out.println(iii + "    (stride >= 0)) {");
650                     out.println(iii + indent + "_weightPointerOES = pointer;");
651                     out.println(iii + "}");
652                 }
653             }
654 
655             boolean isVoid = jfunc.getType().isVoid();
656 
657             if (!isVoid) {
658                 out.println(indent + indent + "return _returnValue;");
659             }
660             out.println(indent + "}");
661         }
662         out.println();
663     }
664 
addNativeRegistration(String s)665     public void addNativeRegistration(String s) {
666         nativeRegistrations.add(s);
667     }
668 
emitNativeRegistration(String registrationFunctionName, PrintStream cStream)669     public void emitNativeRegistration(String registrationFunctionName,
670             PrintStream cStream) {
671         cStream.println("static const char *classPathName = \"" +
672                         mClassPathName +
673                         "\";");
674         cStream.println();
675 
676         cStream.println("static JNINativeMethod methods[] = {");
677 
678         cStream.println("{\"_nativeClassInit\", \"()V\", (void*)nativeClassInit },");
679 
680         Iterator<String> i = nativeRegistrations.iterator();
681         while (i.hasNext()) {
682             cStream.println(i.next());
683         }
684 
685         cStream.println("};");
686         cStream.println();
687 
688 
689         cStream.println("int " + registrationFunctionName + "(JNIEnv *_env)");
690         cStream.println("{");
691         cStream.println(indent +
692                         "int err;");
693 
694         cStream.println(indent +
695                         "err = android::AndroidRuntime::registerNativeMethods(_env, classPathName, methods, NELEM(methods));");
696 
697         cStream.println(indent + "return err;");
698         cStream.println("}");
699     }
700 
JniCodeEmitter()701     public JniCodeEmitter() {
702         super();
703     }
704 
getJniType(JType jType)705     String getJniType(JType jType) {
706         if (jType.isVoid()) {
707             return "void";
708         }
709 
710         String baseType = jType.getBaseType();
711         if (jType.isPrimitive()) {
712             if (baseType.equals("String")) {
713                 return "jstring";
714             } else {
715                 return "j" + baseType;
716             }
717         } else if (jType.isArray()) {
718             return jType.isClass() ? "jobjectArray" : "j" + baseType + "Array";
719         } else {
720             return "jobject";
721         }
722     }
723 
getJniMangledName(String name)724     String getJniMangledName(String name) {
725         name = name.replaceAll("_", "_1");
726         name = name.replaceAll(";", "_2");
727         name = name.replaceAll("\\[", "_3");
728         return name;
729     }
730 
emitJniCode(JFunc jfunc, PrintStream out)731     public void emitJniCode(JFunc jfunc, PrintStream out) {
732         CFunc cfunc = jfunc.getCFunc();
733 
734         // Emit comment identifying original C function
735         //
736         // Example:
737         //
738         // /* void glClipPlanef ( GLenum plane, const GLfloat *equation ) */
739         //
740         out.println("/* " + cfunc.getOriginal() + " */");
741 
742         // Emit JNI signature (name)
743         //
744         // Example:
745         //
746         // void
747         // android_glClipPlanef__I_3FI
748         //
749 
750         String outName = "android_" + jfunc.getName();
751         boolean isPointerFunc = isPointerFunc(jfunc);
752         boolean isPointerOffsetFunc =
753             (outName.endsWith("Pointer") || outName.endsWith("PointerOES") ||
754              outName.endsWith("glDrawElements") ||
755              outName.endsWith("glDrawRangeElements") ||
756              outName.endsWith("glTexImage2D") ||
757              outName.endsWith("glTexSubImage2D") ||
758              outName.endsWith("glCompressedTexImage2D") ||
759              outName.endsWith("glCompressedTexSubImage2D") ||
760              outName.endsWith("glTexImage3D") ||
761              outName.endsWith("glTexSubImage3D") ||
762              outName.endsWith("glCompressedTexImage3D") ||
763              outName.endsWith("glCompressedTexSubImage3D") ||
764              outName.endsWith("glReadPixels"))
765             && !jfunc.getCFunc().hasPointerArg();
766         if (isPointerFunc) {
767             outName += "Bounds";
768         }
769 
770         out.print("static ");
771         out.println(getJniType(jfunc.getType()));
772         out.print(outName);
773 
774         String rsignature = getJniName(jfunc.getType());
775 
776         String signature = "";
777         int numArgs = jfunc.getNumArgs();
778         for (int i = 0; i < numArgs; i++) {
779             JType argType = jfunc.getArgType(i);
780             signature += getJniName(argType);
781         }
782         if (isPointerFunc) {
783             signature += "I";
784         }
785 
786         // Append signature to function name
787         String sig = getJniMangledName(signature).replace('.', '_').replace('/', '_');
788         if (!mUseSimpleMethodNames) {
789             out.print("__" + sig);
790             outName += "__" + sig;
791         }
792 
793         signature = signature.replace('.', '/');
794         rsignature = rsignature.replace('.', '/');
795 
796         out.println();
797         if (rsignature.length() == 0) {
798             rsignature = "V";
799         }
800 
801         String s = "{\"" +
802             jfunc.getName() +
803             (isPointerFunc ? "Bounds" : "") +
804             "\", \"(" + signature +")" +
805             rsignature +
806             "\", (void *) " +
807             outName +
808             " },";
809         nativeRegistrations.add(s);
810 
811         List<Integer> nonPrimitiveArgs = new ArrayList<Integer>();
812         List<Integer> stringArgs = new ArrayList<Integer>();
813         int numBufferArgs = 0;
814         List<String> bufferArgNames = new ArrayList<String>();
815         List<JType> bufferArgTypes = new ArrayList<JType>();
816 
817         // Emit JNI signature (arguments)
818         //
819         // Example:
820         //
821         // (JNIEnv *_env, jobject this, jint plane, jfloatArray equation_ref, jint offset) {
822         //
823         out.print("  (JNIEnv *_env, jobject _this");
824         for (int i = 0; i < numArgs; i++) {
825             out.print(", ");
826             JType argType = jfunc.getArgType(i);
827             String suffix = "";
828             if (!argType.isPrimitive()) {
829                 if (argType.isArray()) {
830                     suffix = "_ref";
831                 } else if (argType.isBuffer()) {
832                     suffix = "_buf";
833                 }
834                 nonPrimitiveArgs.add(new Integer(i));
835                 if (jfunc.getArgType(i).isBuffer()) {
836                     int cIndex = jfunc.getArgCIndex(i);
837                     String cname = cfunc.getArgName(cIndex);
838                     bufferArgNames.add(cname);
839                     bufferArgTypes.add(jfunc.getArgType(i));
840                     numBufferArgs++;
841                 }
842             }
843 
844             if (argType.isString()) {
845                 stringArgs.add(new Integer(i));
846             }
847 
848             out.print(getJniType(argType) + " " + jfunc.getArgName(i) + suffix);
849         }
850         if (isPointerFunc) {
851             out.print(", jint remaining");
852         }
853         out.println(") {");
854 
855         int numArrays = 0;
856         int numBuffers = 0;
857         int numStrings = 0;
858         for (int i = 0; i < nonPrimitiveArgs.size(); i++) {
859             int idx = nonPrimitiveArgs.get(i).intValue();
860             JType argType = jfunc.getArgType(idx);
861             if (argType.isArray()) {
862                 ++numArrays;
863             }
864             if (argType.isBuffer()) {
865                 ++numBuffers;
866             }
867             if (argType.isString()) {
868                 ++numStrings;
869             }
870         }
871 
872         // Emit method body
873 
874         // Emit local variable declarations for _exception and _returnValue
875         //
876         // Example:
877         //
878         // android::gl::ogles_context_t *ctx;
879         //
880         // jint _exception;
881         // GLenum _returnValue;
882         //
883         CType returnType = cfunc.getType();
884         boolean isVoid = returnType.isVoid();
885 
886         boolean isUnsupported = isUnsupportedFunc(cfunc);
887         if (isUnsupported) {
888             out.println(indent +
889                         "jniThrowException(_env, \"java/lang/UnsupportedOperationException\",");
890             out.println(indent +
891                         "    \"" + cfunc.getName() + "\");");
892             if (!isVoid) {
893                 String retval = getErrorReturnValue(cfunc);
894                 if (cfunc.getType().isEGLHandle()) {
895                     String baseType = cfunc.getType().getBaseType().toLowerCase();
896                     out.println(indent +
897                                 "return toEGLHandle(_env, " + baseType + "Class, " +
898                                 baseType + "Constructor, " + retval + ");");
899                 } else {
900                     out.println(indent + "return " + retval + ";");
901                 }
902             }
903             out.println("}");
904             out.println();
905             return;
906         }
907 
908         String requiresExtension = isRequiresFunc(cfunc);
909         if (requiresExtension != null) {
910             out.println(indent +
911                         "if (! supportsExtension(_env, _this, have_" + requiresExtension + "ID)) {");
912             out.println(indent + indent +
913                         "jniThrowException(_env, \"java/lang/UnsupportedOperationException\",");
914             out.println(indent + indent +
915                         "    \"" + cfunc.getName() + "\");");
916             if (isVoid) {
917                 out.println(indent + indent + "    return;");
918             } else {
919                 String retval = getErrorReturnValue(cfunc);
920                 if (cfunc.getType().isEGLHandle()) {
921                     String baseType = cfunc.getType().getBaseType().toLowerCase();
922                     out.println(indent +
923                                 "return toEGLHandle(_env, " + baseType + "Class, " +
924                                 baseType + "Constructor, " + retval + ");");
925                 } else {
926                     out.println(indent + "return " + retval + ";");
927                 }
928             }
929             out.println(indent + "}");
930         }
931         if (mUseContextPointer) {
932             out.println(indent +
933                 "android::gl::ogles_context_t *ctx = getContext(_env, _this);");
934         }
935 
936         boolean initializeReturnValue = stringArgs.size() > 0;
937         boolean emitExceptionCheck = ((numArrays > 0 || numStrings > 0)
938                                              && (hasNonConstArg(jfunc, cfunc, nonPrimitiveArgs)
939                                                  || (cfunc.hasPointerArg() && numArrays > 0))
940                                          || hasCheckTest(cfunc)
941                                          || hasIfTest(cfunc))
942                                          || (stringArgs.size() > 0);
943         // mChecker.getChecks(cfunc.getName()) != null
944         // Emit an _exeption variable if there will be error checks
945         if (emitExceptionCheck) {
946             out.println(indent + "jint _exception = 0;");
947             out.println(indent + "const char * _exceptionType = NULL;");
948             out.println(indent + "const char * _exceptionMessage = NULL;");
949         }
950 
951         // Emit a single _array or multiple _XXXArray variables
952         if (numBufferArgs == 1) {
953             JType bufferType = bufferArgTypes.get(0);
954             if (bufferType.isTypedBuffer()) {
955                 String typedArrayType = getJniType(bufferType.getArrayTypeForTypedBuffer());
956                 out.println(indent + typedArrayType + " _array = (" + typedArrayType + ") 0;");
957             } else {
958                 out.println(indent + "jarray _array = (jarray) 0;");
959             }
960             out.println(indent + "jint _bufferOffset = (jint) 0;");
961         } else {
962             for (int i = 0; i < numBufferArgs; i++) {
963                 JType bufferType = bufferArgTypes.get(0);
964                 if (bufferType.isTypedBuffer()) {
965                     String typedArrayType = getJniType(bufferType.getArrayTypeForTypedBuffer());
966                     out.println(indent + typedArrayType + " _" + bufferArgNames.get(i) +
967                                 "Array = (" + typedArrayType + ") 0;");
968                 } else {
969                     out.println(indent + "jarray _" + bufferArgNames.get(i) +
970                                 "Array = (jarray) 0;");
971                 }
972                 out.println(indent + "jint _" + bufferArgNames.get(i) +
973                             "BufferOffset = (jint) 0;");
974             }
975         }
976         if (!isVoid) {
977             String retval = getErrorReturnValue(cfunc);
978             if (retval != null) {
979                 out.println(indent + returnType.getDeclaration() +
980                             " _returnValue = " + retval + ";");
981             } else if (initializeReturnValue) {
982                 out.println(indent + returnType.getDeclaration() +
983                             " _returnValue = 0;");
984             } else {
985                 out.println(indent + returnType.getDeclaration() +
986                             " _returnValue;");
987             }
988         }
989 
990         // Emit local variable declarations for EGL Handles
991         //
992         // Example:
993         //
994         // EGLSurface surface_native = (EGLHandle)fromEGLHandle(_env, surfaceClass, surfaceConstructor, surface);
995         //
996         if (nonPrimitiveArgs.size() > 0) {
997             for (int i = 0; i < nonPrimitiveArgs.size(); i++) {
998                 int idx = nonPrimitiveArgs.get(i).intValue();
999                 int cIndex = jfunc.getArgCIndex(idx);
1000                 String cname = cfunc.getArgName(cIndex);
1001 
1002                 if (jfunc.getArgType(idx).isBuffer()
1003                    || jfunc.getArgType(idx).isArray()
1004                    || !jfunc.getArgType(idx).isEGLHandle())
1005                     continue;
1006 
1007                 CType type = cfunc.getArgType(jfunc.getArgCIndex(idx));
1008                 String decl = type.getDeclaration();
1009                 out.println(indent +
1010                             decl + " " + cname + "_native = (" +
1011                             decl + ") fromEGLHandle(_env, " +
1012                             type.getBaseType().toLowerCase() +
1013                             "GetHandleID, " + jfunc.getArgName(idx) +
1014                             ");");
1015             }
1016         }
1017 
1018         // Emit local variable declarations for element/sentinel checks
1019         //
1020         // Example:
1021         //
1022         // bool attrib_list_sentinel_found = false;
1023         //
1024         emitLocalVariablesForSentinel(cfunc, out);
1025 
1026         // Emit local variable declarations for pointer arguments
1027         //
1028         // Example:
1029         //
1030         // GLfixed *eqn_base;
1031         // GLfixed *eqn;
1032         //
1033         String offset = "offset";
1034         String remaining = "_remaining";
1035         if (nonPrimitiveArgs.size() > 0) {
1036             for (int i = 0; i < nonPrimitiveArgs.size(); i++) {
1037                 int idx = nonPrimitiveArgs.get(i).intValue();
1038                 int cIndex = jfunc.getArgCIndex(idx);
1039                 String cname = cfunc.getArgName(cIndex);
1040 
1041                 if (!jfunc.getArgType(idx).isBuffer() && !jfunc.getArgType(idx).isArray())
1042                     continue;
1043 
1044                 CType type = cfunc.getArgType(jfunc.getArgCIndex(idx));
1045                 String decl = type.getDeclaration();
1046                 if (jfunc.getArgType(idx).isArray() && !jfunc.getArgType(idx).isClass()) {
1047                     out.println(indent +
1048                                 decl +
1049                                 (decl.endsWith("*") ? "" : " ") +
1050                                 jfunc.getArgName(idx) +
1051                                 "_base = (" + decl + ") 0;");
1052                 }
1053                 remaining = ((numArrays + numBuffers) <= 1) ? "_remaining" :
1054                     "_" + cname + "Remaining";
1055                 out.println(indent +
1056                             "jint " + remaining + ";");
1057                 out.println(indent +
1058                                 decl +
1059                                 (decl.endsWith("*") ? "" : " ") +
1060                                 jfunc.getArgName(idx) +
1061                                 " = (" + decl + ") 0;");
1062             }
1063 
1064             out.println();
1065         }
1066 
1067         // Emit local variable declaration for strings
1068         if (stringArgs.size() > 0) {
1069             for (int i = 0; i < stringArgs.size(); i++) {
1070                 int idx = stringArgs.get(i).intValue();
1071                 int cIndex = jfunc.getArgCIndex(idx);
1072                 String cname = cfunc.getArgName(cIndex);
1073 
1074                 out.println(indent + "const char* _native" + cname + " = 0;");
1075             }
1076 
1077             out.println();
1078         }
1079 
1080         // Null pointer checks and GetStringUTFChars
1081         if (stringArgs.size() > 0) {
1082             for (int i = 0; i < stringArgs.size(); i++) {
1083                 int idx = stringArgs.get(i).intValue();
1084                 int cIndex = jfunc.getArgCIndex(idx);
1085                 String cname = cfunc.getArgName(cIndex);
1086 
1087                 CType type = cfunc.getArgType(jfunc.getArgCIndex(idx));
1088                 String decl = type.getDeclaration();
1089                 needsExit = true;
1090                 out.println(indent + "if (!" + cname + ") {");
1091                 out.println(indent + indent + "_exception = 1;");
1092                 out.println(indent + indent +
1093                             "_exceptionType = \"java/lang/IllegalArgumentException\";");
1094                 out.println(indent + indent +
1095                             "_exceptionMessage = \"" + cname + " == null\";");
1096                 out.println(indent + indent + "goto exit;");
1097                 out.println(indent + "}");
1098 
1099                 out.println(indent + "_native" + cname + " = _env->GetStringUTFChars(" + cname + ", 0);");
1100             }
1101 
1102             out.println();
1103         }
1104 
1105         // Emit 'GetPrimitiveArrayCritical' for non-object arrays
1106         // Emit 'GetPointer' calls for Buffer pointers
1107         if (nonPrimitiveArgs.size() > 0) {
1108             for (int i = 0; i < nonPrimitiveArgs.size(); i++) {
1109                 int idx = nonPrimitiveArgs.get(i).intValue();
1110                 int cIndex = jfunc.getArgCIndex(idx);
1111 
1112                 String cname = cfunc.getArgName(cIndex);
1113                 offset = numArrays <= 1 ? "offset" :
1114                     cname + "Offset";
1115                 remaining = ((numArrays + numBuffers) <= 1) ? "_remaining" :
1116                     "_" + cname + "Remaining";
1117 
1118                 if (jfunc.getArgType(idx).isArray()
1119                        && !jfunc.getArgType(idx).isEGLHandle()) {
1120                     needsExit = true;
1121                     out.println(indent + "if (!" + cname + "_ref) {");
1122                     out.println(indent + indent + "_exception = 1;");
1123                     out.println(indent + indent +
1124                                 "_exceptionType = \"java/lang/IllegalArgumentException\";");
1125                     out.println(indent + indent +
1126                                 "_exceptionMessage = \"" + cname +" == null\";");
1127                     out.println(indent + indent + "goto exit;");
1128                     out.println(indent + "}");
1129                     out.println(indent + "if (" + offset + " < 0) {");
1130                     out.println(indent + indent + "_exception = 1;");
1131                     out.println(indent + indent +
1132                                 "_exceptionType = \"java/lang/IllegalArgumentException\";");
1133                     out.println(indent + indent +
1134                                 "_exceptionMessage = \"" + offset +" < 0\";");
1135                     out.println(indent + indent + "goto exit;");
1136                     out.println(indent + "}");
1137 
1138                     out.println(indent + remaining + " = " +
1139                                     (mUseCPlusPlus ? "_env" : "(*_env)") +
1140                                     "->GetArrayLength(" +
1141                                     (mUseCPlusPlus ? "" : "_env, ") +
1142                                     cname + "_ref) - " + offset + ";");
1143 
1144                     emitNativeBoundsChecks(cfunc, cname, out, false,
1145                                            emitExceptionCheck,
1146                                            offset, remaining, "    ");
1147 
1148                     out.println(indent +
1149                                 cname +
1150                                 "_base = (" +
1151                                 cfunc.getArgType(cIndex).getDeclaration() +
1152                                 ")");
1153                     String arrayGetter = jfunc.getArgType(idx).getArrayGetterForPrimitiveArray();
1154                     out.println(indent + "    " +
1155                                 (mUseCPlusPlus ? "_env" : "(*_env)") +
1156                                 "->" + arrayGetter + "(" +
1157                                 (mUseCPlusPlus ? "" : "_env, ") +
1158                                 jfunc.getArgName(idx) +
1159                                 "_ref, (jboolean *)0);");
1160                     out.println(indent +
1161                                 cname + " = " + cname + "_base + " + offset + ";");
1162 
1163                     emitSentinelCheck(cfunc, cname, out, false,
1164                                       emitExceptionCheck, offset,
1165                                       remaining, indent);
1166                     out.println();
1167                 } else if (jfunc.getArgType(idx).isArray()
1168                               && jfunc.getArgType(idx).isEGLHandle()) {
1169                     needsExit = true;
1170                     out.println(indent + "if (!" + cname + "_ref) {");
1171                     out.println(indent + indent + "_exception = 1;");
1172                     out.println(indent + indent +
1173                                 "_exceptionType = \"java/lang/IllegalArgumentException\";");
1174                     out.println(indent + indent + "_exceptionMessage = \"" + cname +" == null\";");
1175                     out.println(indent + indent + "goto exit;");
1176                     out.println(indent + "}");
1177                     out.println(indent + "if (" + offset + " < 0) {");
1178                     out.println(indent + indent + "_exception = 1;");
1179                     out.println(indent + indent +
1180                                 "_exceptionType = \"java/lang/IllegalArgumentException\";");
1181                     out.println(indent + indent + "_exceptionMessage = \"" + offset +" < 0\";");
1182                     out.println(indent + indent + "goto exit;");
1183                     out.println(indent + "}");
1184 
1185                     out.println(indent + remaining + " = " +
1186                                     (mUseCPlusPlus ? "_env" : "(*_env)") +
1187                                     "->GetArrayLength(" +
1188                                     (mUseCPlusPlus ? "" : "_env, ") +
1189                                     cname + "_ref) - " + offset + ";");
1190                     emitNativeBoundsChecks(cfunc, cname, out, false,
1191                                            emitExceptionCheck,
1192                                            offset, remaining, "    ");
1193                     out.println(indent +
1194                                 jfunc.getArgName(idx) + " = new " +
1195                                 cfunc.getArgType(cIndex).getBaseType() +
1196                                "["+ remaining + "];");
1197                     out.println();
1198                 } else if (jfunc.getArgType(idx).isBuffer()) {
1199                     String array = numBufferArgs <= 1 ? "_array" :
1200                         "_" + cfunc.getArgName(cIndex) + "Array";
1201                     String bufferOffset = numBufferArgs <= 1 ? "_bufferOffset" :
1202                         "_" + cfunc.getArgName(cIndex) + "BufferOffset";
1203 
1204                     boolean nullAllowed = isNullAllowed(cfunc) || isPointerFunc;
1205                     if (nullAllowed) {
1206                         out.println(indent + "if (" + cname + "_buf) {");
1207                         out.print(indent);
1208                     }
1209 
1210                     if (isPointerFunc) {
1211                         out.println(indent +
1212                                 cname +
1213                                 " = (" +
1214                                 cfunc.getArgType(cIndex).getDeclaration() +
1215                                 ") getDirectBufferPointer(_env, " +
1216                                 cname + "_buf);");
1217                         String iii = "    ";
1218                         out.println(iii + indent + "if ( ! " + cname + " ) {");
1219                         out.println(iii + indent + indent + "return;");
1220                         out.println(iii + indent + "}");
1221                     } else {
1222                         out.println(indent +
1223                                     cname +
1224                                     " = (" +
1225                                     cfunc.getArgType(cIndex).getDeclaration() +
1226                                     ")getPointer(_env, " +
1227                                     cname +
1228                                     "_buf, (jarray*)&" + array + ", &" + remaining + ", &" + bufferOffset +
1229                                     ");");
1230                     }
1231 
1232                     emitNativeBoundsChecks(cfunc, cname, out, true,
1233                                            emitExceptionCheck,
1234                                            offset, remaining, nullAllowed ? "        " : "    ");
1235 
1236                     if (nullAllowed) {
1237                         out.println(indent + "}");
1238                     }
1239                 }
1240             }
1241         }
1242 
1243         // Emit 'GetPrimitiveArrayCritical' for pointers if needed
1244         if (nonPrimitiveArgs.size() > 0) {
1245             for (int i = 0; i < nonPrimitiveArgs.size(); i++) {
1246                 int idx = nonPrimitiveArgs.get(i).intValue();
1247                 int cIndex = jfunc.getArgCIndex(idx);
1248 
1249                 if(!jfunc.getArgType(idx).isBuffer() || isPointerFunc) continue;
1250 
1251                 String cname = cfunc.getArgName(cIndex);
1252                 String bufferOffset = numBufferArgs <= 1 ? "_bufferOffset" :
1253                             "_" + cname + "BufferOffset";
1254                 String array = numBufferArgs <= 1 ? "_array" :
1255                             "_" + cfunc.getArgName(cIndex) + "Array";
1256 
1257                 boolean nullAllowed = isNullAllowed(cfunc) || isPointerFunc;
1258                 if (nullAllowed) {
1259                     out.println(indent + "if (" + cname + "_buf && " + cname +" == NULL) {");
1260                 } else {
1261                     out.println(indent + "if (" + cname +" == NULL) {");
1262                 }
1263                 JType argType = jfunc.getArgType(idx);
1264                 if (argType.isTypedBuffer()) {
1265                     String arrayGetter = argType.getArrayTypeForTypedBuffer().getArrayGetterForPrimitiveArray();
1266                     out.println(indent + indent + "char * _" + cname + "Base = (char *)_env->" + arrayGetter + "(" + array + ", (jboolean *) 0);");
1267                     out.println(indent + indent + cname + " = (" +cfunc.getArgType(cIndex).getDeclaration() +") (_" + cname + "Base + " + bufferOffset + ");");
1268                     out.println(indent + "}");
1269                 } else {
1270                     out.println(indent + indent + "char * _" + cname + "Base = (char *)_env->GetPrimitiveArrayCritical(" + array + ", (jboolean *) 0);");
1271                     out.println(indent + indent + cname + " = (" +cfunc.getArgType(cIndex).getDeclaration() +") (_" + cname + "Base + " + bufferOffset + ");");
1272                     out.println(indent + "}");
1273                 }
1274              }
1275         }
1276 
1277 
1278         if (!isVoid) {
1279             out.print(indent + "_returnValue = ");
1280         } else {
1281             out.print(indent);
1282         }
1283         String name = cfunc.getName();
1284 
1285         if (mUseContextPointer) {
1286             name = name.substring(2, name.length()); // Strip off 'gl' prefix
1287             name = name.substring(0, 1).toLowerCase() +
1288                 name.substring(1, name.length());
1289             out.print("ctx->procs.");
1290         }
1291 
1292         out.print(name + (isPointerFunc ? "Bounds" : "") + "(");
1293 
1294         numArgs = cfunc.getNumArgs();
1295         if (numArgs == 0) {
1296             if (mUseContextPointer) {
1297                 out.println("ctx);");
1298             } else {
1299                 out.println(");");
1300             }
1301         } else {
1302             if (mUseContextPointer) {
1303                 out.println("ctx,");
1304             } else {
1305                 out.println();
1306             }
1307             for (int i = 0; i < numArgs; i++) {
1308                 String typecast;
1309                 if (i == numArgs - 1 && isPointerOffsetFunc) {
1310                     typecast = "reinterpret_cast<GLvoid *>";
1311                 } else {
1312                     typecast = "(" + cfunc.getArgType(i).getDeclaration() + ")";
1313                 }
1314                 out.print(indent + indent +
1315                           typecast);
1316 
1317                 if (cfunc.getArgType(i).isConstCharPointer()) {
1318                     out.print("_native");
1319                 }
1320 
1321                 if (cfunc.getArgType(i).isEGLHandle() &&
1322                     !cfunc.getArgType(i).isPointer()){
1323                     out.print(cfunc.getArgName(i)+"_native");
1324                 } else if (i == numArgs - 1 && isPointerOffsetFunc){
1325                     out.print("("+cfunc.getArgName(i)+")");
1326                 } else {
1327                     out.print(cfunc.getArgName(i));
1328                 }
1329 
1330                 if (i == numArgs - 1) {
1331                     if (isPointerFunc) {
1332                         out.println(",");
1333                         out.println(indent + indent + "(GLsizei)remaining");
1334                     } else {
1335                         out.println();
1336                     }
1337                 } else {
1338                     out.println(",");
1339                 }
1340             }
1341             out.println(indent + ");");
1342         }
1343 
1344         if (needsExit) {
1345             out.println();
1346             out.println("exit:");
1347             needsExit = false;
1348         }
1349 
1350 
1351         if (nonPrimitiveArgs.size() > 0) {
1352             for (int i = nonPrimitiveArgs.size() - 1; i >= 0; i--) {
1353                 int idx = nonPrimitiveArgs.get(i).intValue();
1354 
1355                 int cIndex = jfunc.getArgCIndex(idx);
1356                 if (jfunc.getArgType(idx).isArray() && !jfunc.getArgType(idx).isClass()) {
1357 
1358                     // If the argument is 'const', GL will not write to it.
1359                     // In this case, we can use the 'JNI_ABORT' flag to avoid
1360                     // the need to write back to the Java array
1361                     out.println(indent +
1362                                 "if (" + jfunc.getArgName(idx) + "_base) {");
1363                     String arrayReleaser = jfunc.getArgType(idx).getArrayReleaserForPrimitiveArray();
1364                     out.println(indent + indent +
1365                                 (mUseCPlusPlus ? "_env" : "(*_env)") +
1366                                 "->" + arrayReleaser + "(" +
1367                                 (mUseCPlusPlus ? "" : "_env, ") +
1368                                 jfunc.getArgName(idx) + "_ref, " +
1369                                 "(j" + jfunc.getArgType(idx).getBaseType() + "*)" + cfunc.getArgName(cIndex) +
1370                                 "_base,");
1371                     out.println(indent + indent + indent +
1372                                 (cfunc.getArgType(cIndex).isConst() ?
1373                                  "JNI_ABORT" : "_exception ? JNI_ABORT: 0" ) +
1374                                 ");");
1375                     out.println(indent + "}");
1376                 } else if (jfunc.getArgType(idx).isBuffer()) {
1377                     if (! isPointerFunc) {
1378                         JType argType = jfunc.getArgType(idx);
1379                         String array = numBufferArgs <= 1 ? "_array" :
1380                             "_" + cfunc.getArgName(cIndex) + "Array";
1381                         out.println(indent + "if (" + array + ") {");
1382                         if (argType.isTypedBuffer()) {
1383                             String arrayReleaser =
1384                                 argType.getArrayTypeForTypedBuffer().getArrayReleaserForPrimitiveArray();
1385                             out.println(indent + indent +
1386                                 "_env->" + arrayReleaser + "(" + array + ", " +
1387                                 "(j" + argType.getArrayTypeForTypedBuffer().getBaseType() + "*)" +
1388                                 cfunc.getArgName(cIndex) +
1389                                 ", " +
1390                                 (cfunc.getArgType(cIndex).isConst() ?
1391                                     "JNI_ABORT" : (emitExceptionCheck ?
1392                                         "_exception ? JNI_ABORT : 0" : "0")) +
1393                                 ");");
1394                         } else {
1395                             out.println(indent + indent +
1396                                 "releasePointer(_env, " + array + ", " +
1397                                 cfunc.getArgName(cIndex) +
1398                                 ", " +
1399                                 (cfunc.getArgType(cIndex).isConst() ?
1400                                     "JNI_FALSE" : (emitExceptionCheck ?
1401                                         "_exception ? JNI_FALSE : JNI_TRUE" : "JNI_TRUE")) +
1402                                 ");");
1403                         }
1404                         out.println(indent + "}");
1405                     }
1406                 }
1407             }
1408         }
1409 
1410         // Emit local variable declaration for strings
1411         if (stringArgs.size() > 0) {
1412             for (int i = 0; i < stringArgs.size(); i++) {
1413                 int idx = stringArgs.get(i).intValue();
1414                 int cIndex = jfunc.getArgCIndex(idx);
1415                 String cname = cfunc.getArgName(cIndex);
1416 
1417                 out.println(indent + "if (_native" + cname + ") {");
1418                 out.println(indent + "    _env->ReleaseStringUTFChars(" + cname + ", _native" + cname + ");");
1419                 out.println(indent + "}");
1420             }
1421 
1422             out.println();
1423         }
1424 
1425         // Copy results back to java arrays
1426        if (nonPrimitiveArgs.size() > 0) {
1427             for (int i = nonPrimitiveArgs.size() - 1; i >= 0; i--) {
1428                 int idx = nonPrimitiveArgs.get(i).intValue();
1429                 int cIndex = jfunc.getArgCIndex(idx);
1430                 String baseType = cfunc.getArgType(cIndex).getBaseType().toLowerCase();
1431                 if (jfunc.getArgType(idx).isArray() && jfunc.getArgType(idx).isClass()) {
1432                     remaining  = ((numArrays + numBuffers) <= 1) ? "_remaining" :
1433                                      "_" + cfunc.getArgName(cIndex) + "Remaining";
1434                     offset = numArrays <= 1 ? "offset" : cfunc.getArgName(cIndex) + "Offset";
1435                     out.println(indent +
1436                                 "if (" + jfunc.getArgName(idx) + ") {");
1437                     out.println(indent + indent +
1438                                 "for (int i = 0; i < " + remaining + "; i++) {");
1439                     out.println(indent + indent + indent +
1440                                 "jobject " + cfunc.getArgName(cIndex) +
1441                                 "_new = toEGLHandle(_env, " + baseType +
1442                                 "Class, " + baseType + "Constructor, " +
1443                                 cfunc.getArgName(cIndex) + "[i]);");
1444                     out.println(indent + indent + indent +
1445                                 (mUseCPlusPlus ? "_env" : "(*_env)") +
1446                                 "->SetObjectArrayElement(" +
1447                                 (mUseCPlusPlus ? "" : "_env, ") +
1448                                 cfunc.getArgName(cIndex) +
1449                                 "_ref, i + " + offset + ", " +
1450                                 cfunc.getArgName(cIndex) + "_new);");
1451                     out.println(indent + indent + "}");
1452                     out.println(indent + indent +
1453                                 "delete[] " + jfunc.getArgName(idx) + ";");
1454                     out.println(indent + "}");
1455                 }
1456             }
1457         }
1458 
1459 
1460         // Throw exception if there is one
1461         if (emitExceptionCheck) {
1462             out.println(indent + "if (_exception) {");
1463             out.println(indent + indent +
1464                         "jniThrowException(_env, _exceptionType, _exceptionMessage);");
1465             out.println(indent + "}");
1466 
1467         }
1468 
1469 
1470         if (!isVoid) {
1471             if (cfunc.getType().isEGLHandle()) {
1472                 String baseType = cfunc.getType().getBaseType().toLowerCase();
1473                 out.println(indent +
1474                             "return toEGLHandle(_env, " + baseType + "Class, " +
1475                             baseType + "Constructor, _returnValue);");
1476             } else {
1477                 out.println(indent + "return (" +
1478                             getJniType(jfunc.getType()) + ")_returnValue;");
1479             }
1480         }
1481 
1482         out.println("}");
1483         out.println();
1484     }
1485 
1486 }
1487