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