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