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