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