Home | History | Annotate | Download | only in src
      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 
     17 import java.io.PrintStream;
     18 import java.util.ArrayList;
     19 import java.util.HashSet;
     20 import java.util.Iterator;
     21 import java.util.List;
     22 
     23 public 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