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