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("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")) &&
    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