• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
getJniName(JType jType)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 
emitCode(CFunc cfunc, String original, PrintStream javaInterfaceStream, PrintStream javaImplStream, PrintStream cStream)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 
emitNativeDeclaration(JFunc jfunc, PrintStream out)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 
emitJavaInterfaceCode(JFunc jfunc, PrintStream out)130     public void emitJavaInterfaceCode(JFunc jfunc, PrintStream out) {
131         emitFunction(jfunc, out, false, true);
132     }
133 
emitJavaCode(JFunc jfunc, PrintStream out)134     public void emitJavaCode(JFunc jfunc, PrintStream out) {
135         emitFunction(jfunc, out, false, false);
136     }
137 
isPointerFunc(JFunc jfunc)138     boolean isPointerFunc(JFunc jfunc) {
139         String name = jfunc.getName();
140         return (name.endsWith("Pointer") || name.endsWith("PointerOES"))
141             && jfunc.getCFunc().hasPointerArg();
142     }
143 
emitFunctionCall(JFunc jfunc, PrintStream out, String iii, boolean grabArray)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 
printIfcheckPostamble(PrintStream out, boolean isBuffer, boolean emitExceptionCheck, String iii)186     void printIfcheckPostamble(PrintStream out, boolean isBuffer, boolean emitExceptionCheck,
187             String iii) {
188                 printIfcheckPostamble(out, isBuffer, emitExceptionCheck,
189                                       "offset", "_remaining", iii);
190             }
191 
printIfcheckPostamble(PrintStream out, boolean isBuffer, boolean emitExceptionCheck, String offset, String remaining, String iii)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 
isNullAllowed(CFunc cfunc)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 
getErrorReturnValue(CFunc cfunc)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 
isUnsupportedFunc(CFunc cfunc)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 
isRequiresFunc(CFunc cfunc)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 
emitNativeBoundsChecks(CFunc cfunc, String cname, PrintStream out, boolean isBuffer, boolean emitExceptionCheck, String offset, String remaining, String iii)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 
hasNonConstArg(JFunc jfunc, CFunc cfunc, List<Integer> nonPrimitiveArgs)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      */
emitFunction(JFunc jfunc, PrintStream out, boolean nativeDecl, boolean interfaceDecl)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 
addNativeRegistration(String s)609     public void addNativeRegistration(String s) {
610         nativeRegistrations.add(s);
611     }
612 
emitNativeRegistration(String registrationFunctionName, PrintStream cStream)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 
JniCodeEmitter()645     public JniCodeEmitter() {
646         super();
647     }
648 
getJniType(JType jType)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 
getJniMangledName(String name)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 
emitJniCode(JFunc jfunc, PrintStream out)675     public void emitJniCode(JFunc jfunc, PrintStream out) {
676         CFunc cfunc = jfunc.getCFunc();
677 
678         // Emit comment identifying original C function
679         //
680         // Example:
681         //
682         // /* void glClipPlanef ( GLenum plane, const GLfloat *equation ) */
683         //
684         out.println("/* " + cfunc.getOriginal() + " */");
685 
686         // Emit JNI signature (name)
687         //
688         // Example:
689         //
690         // void
691         // android_glClipPlanef__I_3FI
692         //
693 
694         String outName = "android_" + jfunc.getName();
695         boolean isPointerFunc = isPointerFunc(jfunc);
696         boolean isVBOPointerFunc = (outName.endsWith("Pointer") ||
697                 outName.endsWith("PointerOES") ||
698             outName.endsWith("DrawElements") || outName.endsWith("VertexAttribPointer")) &&
699             !jfunc.getCFunc().hasPointerArg();
700         if (isPointerFunc) {
701             outName += "Bounds";
702         }
703 
704         out.print("static ");
705         out.println(getJniType(jfunc.getType()));
706         out.print(outName);
707 
708         String rsignature = getJniName(jfunc.getType());
709 
710         String signature = "";
711         int numArgs = jfunc.getNumArgs();
712         for (int i = 0; i < numArgs; i++) {
713             JType argType = jfunc.getArgType(i);
714             signature += getJniName(argType);
715         }
716         if (isPointerFunc) {
717             signature += "I";
718         }
719 
720         // Append signature to function name
721         String sig = getJniMangledName(signature).replace('.', '_').replace('/', '_');
722         out.print("__" + sig);
723         outName += "__" + sig;
724 
725         signature = signature.replace('.', '/');
726         rsignature = rsignature.replace('.', '/');
727 
728         out.println();
729         if (rsignature.length() == 0) {
730             rsignature = "V";
731         }
732 
733         String s = "{\"" +
734             jfunc.getName() +
735             (isPointerFunc ? "Bounds" : "") +
736             "\", \"(" + signature +")" +
737             rsignature +
738             "\", (void *) " +
739             outName +
740             " },";
741         nativeRegistrations.add(s);
742 
743         List<Integer> nonPrimitiveArgs = new ArrayList<Integer>();
744         List<Integer> stringArgs = new ArrayList<Integer>();
745         int numBufferArgs = 0;
746         List<String> bufferArgNames = new ArrayList<String>();
747 
748         // Emit JNI signature (arguments)
749         //
750         // Example:
751         //
752         // (JNIEnv *_env, jobject this, jint plane, jfloatArray equation_ref, jint offset) {
753         //
754         out.print("  (JNIEnv *_env, jobject _this");
755         for (int i = 0; i < numArgs; i++) {
756             out.print(", ");
757             JType argType = jfunc.getArgType(i);
758             String suffix;
759             if (!argType.isPrimitive()) {
760                 if (argType.isArray()) {
761                     suffix = "_ref";
762                 } else {
763                     suffix = "_buf";
764                 }
765                 nonPrimitiveArgs.add(new Integer(i));
766                 if (jfunc.getArgType(i).isBuffer()) {
767                     int cIndex = jfunc.getArgCIndex(i);
768                     String cname = cfunc.getArgName(cIndex);
769                     bufferArgNames.add(cname);
770                     numBufferArgs++;
771                 }
772             } else {
773                 suffix = "";
774             }
775             if (argType.isString()) {
776                 stringArgs.add(new Integer(i));
777             }
778 
779             out.print(getJniType(argType) + " " + jfunc.getArgName(i) + suffix);
780         }
781         if (isPointerFunc) {
782             out.print(", jint remaining");
783         }
784         out.println(") {");
785 
786         int numArrays = 0;
787         int numBuffers = 0;
788         int numStrings = 0;
789         for (int i = 0; i < nonPrimitiveArgs.size(); i++) {
790             int idx = nonPrimitiveArgs.get(i).intValue();
791             JType argType = jfunc.getArgType(idx);
792             if (argType.isArray()) {
793                 ++numArrays;
794             }
795             if (argType.isBuffer()) {
796                 ++numBuffers;
797             }
798             if (argType.isString()) {
799                 ++numStrings;
800             }
801         }
802 
803         // Emit method body
804 
805         // Emit local variable declarations for _exception and _returnValue
806         //
807         // Example:
808         //
809         // android::gl::ogles_context_t *ctx;
810         //
811         // jint _exception;
812         // GLenum _returnValue;
813         //
814         CType returnType = cfunc.getType();
815         boolean isVoid = returnType.isVoid();
816 
817         boolean isUnsupported = isUnsupportedFunc(cfunc);
818         if (isUnsupported) {
819             out.println(indent +
820                         "_env->ThrowNew(UOEClass,");
821             out.println(indent +
822                         "    \"" + cfunc.getName() + "\");");
823             if (!isVoid) {
824                 String retval = getErrorReturnValue(cfunc);
825                 out.println(indent + "return " + retval + ";");
826             }
827             out.println("}");
828             out.println();
829             return;
830         }
831 
832         String requiresExtension = isRequiresFunc(cfunc);
833         if (requiresExtension != null) {
834             out.println(indent +
835                         "if (! supportsExtension(_env, _this, have_" + requiresExtension + "ID)) {");
836             out.println(indent + indent +
837                         "_env->ThrowNew(UOEClass,");
838             out.println(indent + indent +
839                         "    \"" + cfunc.getName() + "\");");
840             if (isVoid) {
841                 out.println(indent + indent + "    return;");
842             } else {
843                 String retval = getErrorReturnValue(cfunc);
844                 out.println(indent + indent + "    return " + retval + ";");
845             }
846             out.println(indent + "}");
847         }
848         if (mUseContextPointer) {
849             out.println(indent +
850                 "android::gl::ogles_context_t *ctx = getContext(_env, _this);");
851         }
852 
853         boolean initializeReturnValue = stringArgs.size() > 0;
854 
855         boolean emitExceptionCheck = (numArrays > 0 || numBuffers > 0 || numStrings > 0) &&
856             hasNonConstArg(jfunc, cfunc, nonPrimitiveArgs);
857         // mChecker.getChecks(cfunc.getName()) != null
858 
859         // Emit an _exeption variable if there will be error checks
860         if (emitExceptionCheck) {
861             out.println(indent + "jint _exception = 0;");
862         }
863 
864         // Emit a single _array or multiple _XXXArray variables
865         if (numBufferArgs == 1) {
866                 out.println(indent + "jarray _array = (jarray) 0;");
867         } else {
868             for (int i = 0; i < numBufferArgs; i++) {
869                 out.println(indent + "jarray _" + bufferArgNames.get(i) +
870                             "Array = (jarray) 0;");
871             }
872         }
873         if (!isVoid) {
874             String retval = getErrorReturnValue(cfunc);
875             if (retval != null) {
876                 out.println(indent + returnType.getDeclaration() +
877                             " _returnValue = " + retval + ";");
878             } else if (initializeReturnValue) {
879                 out.println(indent + returnType.getDeclaration() +
880                 " _returnValue = 0;");
881             } else {
882                 out.println(indent + returnType.getDeclaration() +
883                             " _returnValue;");
884             }
885         }
886 
887         // Emit local variable declarations for pointer arguments
888         //
889         // Example:
890         //
891         // GLfixed *eqn_base;
892         // GLfixed *eqn;
893         //
894         String offset = "offset";
895         String remaining = "_remaining";
896         if (nonPrimitiveArgs.size() > 0) {
897             for (int i = 0; i < nonPrimitiveArgs.size(); i++) {
898                 int idx = nonPrimitiveArgs.get(i).intValue();
899                 int cIndex = jfunc.getArgCIndex(idx);
900                 String cname = cfunc.getArgName(cIndex);
901 
902                 CType type = cfunc.getArgType(jfunc.getArgCIndex(idx));
903                 String decl = type.getDeclaration();
904                 if (jfunc.getArgType(idx).isArray()) {
905                     out.println(indent +
906                                 decl +
907                                 (decl.endsWith("*") ? "" : " ") +
908                                 jfunc.getArgName(idx) +
909                                 "_base = (" + decl + ") 0;");
910                 }
911                 remaining = ((numArrays + numBuffers) <= 1) ? "_remaining" :
912                     "_" + cname + "Remaining";
913                 out.println(indent +
914                             "jint " + remaining + ";");
915                 out.println(indent +
916                             decl +
917                             (decl.endsWith("*") ? "" : " ") +
918                             jfunc.getArgName(idx) +
919                             " = (" + decl + ") 0;");
920             }
921 
922             out.println();
923         }
924 
925         // Emit local variable declaration for strings
926         if (stringArgs.size() > 0) {
927             for (int i = 0; i < stringArgs.size(); i++) {
928                 int idx = stringArgs.get(i).intValue();
929                 int cIndex = jfunc.getArgCIndex(idx);
930                 String cname = cfunc.getArgName(cIndex);
931 
932                 out.println(indent + "const char* _native" + cname + " = 0;");
933             }
934 
935             out.println();
936         }
937 
938         // Null pointer checks and GetStringUTFChars
939         if (stringArgs.size() > 0) {
940             for (int i = 0; i < stringArgs.size(); i++) {
941                 int idx = stringArgs.get(i).intValue();
942                 int cIndex = jfunc.getArgCIndex(idx);
943                 String cname = cfunc.getArgName(cIndex);
944 
945                 CType type = cfunc.getArgType(jfunc.getArgCIndex(idx));
946                 String decl = type.getDeclaration();
947                 out.println(indent + "if (!" + cname + ") {");
948                 out.println(indent + "    _env->ThrowNew(IAEClass, \"" + cname + " == null\");");
949                 out.println(indent + "    goto exit;");
950                 needsExit = true;
951                 out.println(indent + "}");
952 
953                 out.println(indent + "_native" + cname + " = _env->GetStringUTFChars(" + cname + ", 0);");
954             }
955 
956             out.println();
957         }
958 
959         // Emit 'GetPrimitiveArrayCritical' for arrays
960         // Emit 'GetPointer' calls for Buffer pointers
961         int bufArgIdx = 0;
962         if (nonPrimitiveArgs.size() > 0) {
963             for (int i = 0; i < nonPrimitiveArgs.size(); i++) {
964                 int idx = nonPrimitiveArgs.get(i).intValue();
965                 int cIndex = jfunc.getArgCIndex(idx);
966 
967                 String cname = cfunc.getArgName(cIndex);
968                 offset = numArrays <= 1 ? "offset" :
969                     cname + "Offset";
970                 remaining = ((numArrays + numBuffers) <= 1) ? "_remaining" :
971                     "_" + cname + "Remaining";
972 
973                 if (jfunc.getArgType(idx).isArray()) {
974                     out.println(indent +
975                                 "if (!" +
976                                 cname +
977                                 "_ref) {");
978                     if (emitExceptionCheck) {
979                         out.println(indent + indent + "_exception = 1;");
980                     }
981                     out.println(indent + "    " +
982                                 (mUseCPlusPlus ? "_env" : "(*_env)") +
983                                 "->ThrowNew(" +
984                                 (mUseCPlusPlus ? "" : "_env, ") +
985                                 "IAEClass, " +
986                                 "\"" + cname +
987                                 " == null\");");
988                     out.println(indent + "    goto exit;");
989                     needsExit = true;
990                     out.println(indent + "}");
991 
992                     out.println(indent + "if (" + offset + " < 0) {");
993                     if (emitExceptionCheck) {
994                         out.println(indent + indent + "_exception = 1;");
995                     }
996                     out.println(indent + "    " +
997                                 (mUseCPlusPlus ? "_env" : "(*_env)") +
998                                 "->ThrowNew(" +
999                                 (mUseCPlusPlus ? "" : "_env, ") +
1000                                 "IAEClass, " +
1001                                 "\"" + offset + " < 0\");");
1002                     out.println(indent + "    goto exit;");
1003                     needsExit = true;
1004                     out.println(indent + "}");
1005 
1006                     out.println(indent + remaining + " = " +
1007                                     (mUseCPlusPlus ? "_env" : "(*_env)") +
1008                                     "->GetArrayLength(" +
1009                                     (mUseCPlusPlus ? "" : "_env, ") +
1010                                     cname + "_ref) - " + offset + ";");
1011 
1012                     emitNativeBoundsChecks(cfunc, cname, out, false,
1013                                            emitExceptionCheck,
1014                                            offset, remaining, "    ");
1015 
1016                     out.println(indent +
1017                                 cname +
1018                                 "_base = (" +
1019                                 cfunc.getArgType(cIndex).getDeclaration() +
1020                                 ")");
1021                     out.println(indent + "    " +
1022                                 (mUseCPlusPlus ? "_env" : "(*_env)") +
1023                                 "->GetPrimitiveArrayCritical(" +
1024                                 (mUseCPlusPlus ? "" : "_env, ") +
1025                                 jfunc.getArgName(idx) +
1026                                 "_ref, (jboolean *)0);");
1027                     out.println(indent +
1028                                 cname + " = " + cname + "_base + " + offset +
1029                                 ";");
1030                     out.println();
1031                 } else {
1032                     String array = numBufferArgs <= 1 ? "_array" :
1033                         "_" + bufferArgNames.get(bufArgIdx++) + "Array";
1034 
1035                     boolean nullAllowed = isNullAllowed(cfunc) || isPointerFunc;
1036                     if (nullAllowed) {
1037                         out.println(indent + "if (" + cname + "_buf) {");
1038                         out.print(indent);
1039                     }
1040 
1041                     if (isPointerFunc) {
1042                         out.println(indent +
1043                                 cname +
1044                                 " = (" +
1045                                 cfunc.getArgType(cIndex).getDeclaration() +
1046                                 ") getDirectBufferPointer(_env, " +
1047                                 cname + "_buf);");
1048                         String iii = "    ";
1049                         out.println(iii + indent + "if ( ! " + cname + " ) {");
1050                         out.println(iii + iii + indent + "return;");
1051                         out.println(iii + indent + "}");
1052                     } else {
1053                         out.println(indent +
1054                                     cname +
1055                                     " = (" +
1056                                     cfunc.getArgType(cIndex).getDeclaration() +
1057                                     ")getPointer(_env, " +
1058                                     cname +
1059                                     "_buf, &" + array + ", &" + remaining +
1060                                     ");");
1061                     }
1062 
1063                     emitNativeBoundsChecks(cfunc, cname, out, true,
1064                                            emitExceptionCheck,
1065                                            offset, remaining, nullAllowed ? "        " : "    ");
1066 
1067                     if (nullAllowed) {
1068                         out.println(indent + "}");
1069                     }
1070                 }
1071             }
1072         }
1073 
1074         if (!isVoid) {
1075             out.print(indent + "_returnValue = ");
1076         } else {
1077             out.print(indent);
1078         }
1079         String name = cfunc.getName();
1080 
1081         if (mUseContextPointer) {
1082             name = name.substring(2, name.length()); // Strip off 'gl' prefix
1083             name = name.substring(0, 1).toLowerCase() +
1084                 name.substring(1, name.length());
1085             out.print("ctx->procs.");
1086         }
1087 
1088         out.print(name + (isPointerFunc ? "Bounds" : "") + "(");
1089 
1090         numArgs = cfunc.getNumArgs();
1091         if (numArgs == 0) {
1092             if (mUseContextPointer) {
1093                 out.println("ctx);");
1094             } else {
1095                 out.println(");");
1096             }
1097         } else {
1098             if (mUseContextPointer) {
1099                 out.println("ctx,");
1100             } else {
1101                 out.println();
1102             }
1103             for (int i = 0; i < numArgs; i++) {
1104                 String typecast;
1105                 if (i == numArgs - 1 && isVBOPointerFunc) {
1106                     typecast = "const GLvoid *";
1107                 } else {
1108                     typecast = cfunc.getArgType(i).getDeclaration();
1109                 }
1110                 out.print(indent + indent +
1111                           "(" +
1112                           typecast +
1113                           ")");
1114                 if (cfunc.getArgType(i).isConstCharPointer()) {
1115                     out.print("_native");
1116                 }
1117                 out.print(cfunc.getArgName(i));
1118 
1119                 if (i == numArgs - 1) {
1120                     if (isPointerFunc) {
1121                         out.println(",");
1122                         out.println(indent + indent + "(GLsizei)remaining");
1123                     } else {
1124                         out.println();
1125                     }
1126                 } else {
1127                     out.println(",");
1128                 }
1129             }
1130             out.println(indent + ");");
1131         }
1132 
1133         if (needsExit) {
1134             out.println();
1135             out.println("exit:");
1136             needsExit = false;
1137         }
1138 
1139         bufArgIdx = 0;
1140         if (nonPrimitiveArgs.size() > 0) {
1141             for (int i = nonPrimitiveArgs.size() - 1; i >= 0; i--) {
1142                 int idx = nonPrimitiveArgs.get(i).intValue();
1143 
1144                 int cIndex = jfunc.getArgCIndex(idx);
1145                 if (jfunc.getArgType(idx).isArray()) {
1146 
1147                     // If the argument is 'const', GL will not write to it.
1148                     // In this case, we can use the 'JNI_ABORT' flag to avoid
1149                     // the need to write back to the Java array
1150                     out.println(indent +
1151                                 "if (" + jfunc.getArgName(idx) + "_base) {");
1152                     out.println(indent + indent +
1153                                 (mUseCPlusPlus ? "_env" : "(*_env)") +
1154                                 "->ReleasePrimitiveArrayCritical(" +
1155                                 (mUseCPlusPlus ? "" : "_env, ") +
1156                                 jfunc.getArgName(idx) + "_ref, " +
1157                                 cfunc.getArgName(cIndex) +
1158                                 "_base,");
1159                     out.println(indent + indent + indent +
1160                                 (cfunc.getArgType(cIndex).isConst() ?
1161                                  "JNI_ABORT" :
1162                                  "_exception ? JNI_ABORT: 0") +
1163                                 ");");
1164                     out.println(indent + "}");
1165                 } else if (jfunc.getArgType(idx).isBuffer()) {
1166                     if (! isPointerFunc) {
1167                         String array = numBufferArgs <= 1 ? "_array" :
1168                             "_" + bufferArgNames.get(bufArgIdx++) + "Array";
1169                         out.println(indent + "if (" + array + ") {");
1170                         out.println(indent + indent +
1171                                     "releasePointer(_env, " + array + ", " +
1172                                     cfunc.getArgName(cIndex) +
1173                                     ", " +
1174                                     (cfunc.getArgType(cIndex).isConst() ?
1175                                      "JNI_FALSE" : "_exception ? JNI_FALSE :" +
1176                                              " JNI_TRUE") +
1177                                     ");");
1178                         out.println(indent + "}");
1179                     }
1180                 }
1181             }
1182         }
1183 
1184         // Emit local variable declaration for strings
1185         if (stringArgs.size() > 0) {
1186             for (int i = 0; i < stringArgs.size(); i++) {
1187                 int idx = stringArgs.get(i).intValue();
1188                 int cIndex = jfunc.getArgCIndex(idx);
1189                 String cname = cfunc.getArgName(cIndex);
1190 
1191                 out.println(indent + "if (_native" + cname + ") {");
1192                 out.println(indent + "    _env->ReleaseStringUTFChars(" + cname + ", _native" + cname + ");");
1193                 out.println(indent + "}");
1194             }
1195 
1196             out.println();
1197         }
1198 
1199 
1200         if (!isVoid) {
1201             out.println(indent + "return _returnValue;");
1202         }
1203 
1204         out.println("}");
1205         out.println();
1206     }
1207 
1208 }
1209