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