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