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