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