blob: 4c1814a7e299f2f95f309c1e6bb666a82aa607c4 [file] [log] [blame]
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -08001import java.io.PrintStream;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -08002import java.util.ArrayList;
3import java.util.HashSet;
4import java.util.Iterator;
5import java.util.List;
6
Jack Palevichffac1ef2009-04-14 19:00:09 -07007public class JniCodeEmitter {
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -08008
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -08009 static final boolean mUseCPlusPlus = true;
Jack Palevichffac1ef2009-04-14 19:00:09 -070010 protected boolean mUseContextPointer = true;
Jack Palevich427f5852009-04-15 19:13:17 -070011 protected boolean mUseStaticMethods = false;
Jack Palevichffac1ef2009-04-14 19:00:09 -070012 protected String mClassPathName;
13 protected ParameterChecker mChecker;
14 protected List<String> nativeRegistrations = new ArrayList<String>();
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080015 boolean needsExit;
Jack Palevichffac1ef2009-04-14 19:00:09 -070016 protected static String indent = " ";
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080017 HashSet<String> mFunctionsEmitted = new HashSet<String>();
18
Jack Palevichffac1ef2009-04-14 19:00:09 -070019 public static String getJniName(JType jType) {
20 String jniName = "";
21 if (jType.isClass()) {
22 return "L" + jType.getBaseType() + ";";
23 } else if (jType.isArray()) {
24 jniName = "[";
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080025 }
Jack Palevichffac1ef2009-04-14 19:00:09 -070026
27 String baseType = jType.getBaseType();
28 if (baseType.equals("int")) {
29 jniName += "I";
30 } else if (baseType.equals("float")) {
31 jniName += "F";
32 } else if (baseType.equals("boolean")) {
33 jniName += "Z";
34 } else if (baseType.equals("short")) {
35 jniName += "S";
36 } else if (baseType.equals("long")) {
37 jniName += "L";
38 } else if (baseType.equals("byte")) {
39 jniName += "B";
Jack Palevich50d0b142009-11-19 16:34:55 +080040 } else if (baseType.equals("String")) {
41 jniName += "Ljava/lang/String;";
42 } else if (baseType.equals("void")) {
43 // nothing.
44 } else {
45 throw new RuntimeException("Uknown primitive basetype " + baseType);
Jack Palevichffac1ef2009-04-14 19:00:09 -070046 }
47 return jniName;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080048 }
49
Jack Palevichffac1ef2009-04-14 19:00:09 -070050
51 public void emitCode(CFunc cfunc, String original,
52 PrintStream javaInterfaceStream,
53 PrintStream javaImplStream,
54 PrintStream cStream) {
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080055 JFunc jfunc;
56 String signature;
57 boolean duplicate;
Jack Palevich6cbca502009-04-13 16:22:25 -070058
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080059 if (cfunc.hasTypedPointerArg()) {
60 jfunc = JFunc.convert(cfunc, true);
61
62 // Don't emit duplicate functions
63 // These may appear because they are defined in multiple
64 // Java interfaces (e.g., GL11/GL11ExtensionPack)
65 signature = jfunc.toString();
66 duplicate = false;
67 if (mFunctionsEmitted.contains(signature)) {
68 duplicate = true;
69 } else {
70 mFunctionsEmitted.add(signature);
71 }
72
73 if (!duplicate) {
Jack Palevichffac1ef2009-04-14 19:00:09 -070074 emitNativeDeclaration(jfunc, javaImplStream);
75 emitJavaCode(jfunc, javaImplStream);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080076 }
Jack Palevich427f5852009-04-15 19:13:17 -070077 if (javaInterfaceStream != null) {
78 emitJavaInterfaceCode(jfunc, javaInterfaceStream);
79 }
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080080 if (!duplicate) {
Jack Palevichffac1ef2009-04-14 19:00:09 -070081 emitJniCode(jfunc, cStream);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080082 }
83 }
84
85 jfunc = JFunc.convert(cfunc, false);
86
87 signature = jfunc.toString();
88 duplicate = false;
89 if (mFunctionsEmitted.contains(signature)) {
90 duplicate = true;
91 } else {
92 mFunctionsEmitted.add(signature);
93 }
94
95 if (!duplicate) {
Jack Palevichffac1ef2009-04-14 19:00:09 -070096 emitNativeDeclaration(jfunc, javaImplStream);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080097 }
Jack Palevich427f5852009-04-15 19:13:17 -070098 if (javaInterfaceStream != null) {
99 emitJavaInterfaceCode(jfunc, javaInterfaceStream);
100 }
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800101 if (!duplicate) {
Jack Palevichffac1ef2009-04-14 19:00:09 -0700102 emitJavaCode(jfunc, javaImplStream);
103 emitJniCode(jfunc, cStream);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800104 }
105 }
106
107 public void emitNativeDeclaration(JFunc jfunc, PrintStream out) {
108 out.println(" // C function " + jfunc.getCFunc().getOriginal());
109 out.println();
110
111 emitFunction(jfunc, out, true, false);
112 }
113
114 public void emitJavaInterfaceCode(JFunc jfunc, PrintStream out) {
115 emitFunction(jfunc, out, false, true);
116 }
117
118 public void emitJavaCode(JFunc jfunc, PrintStream out) {
119 emitFunction(jfunc, out, false, false);
120 }
Jack Palevich6cbca502009-04-13 16:22:25 -0700121
Jack Palevichffac1ef2009-04-14 19:00:09 -0700122 void emitFunctionCall(JFunc jfunc, PrintStream out, String iii, boolean grabArray) {
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800123 boolean isVoid = jfunc.getType().isVoid();
124 boolean isPointerFunc = jfunc.getName().endsWith("Pointer") &&
125 jfunc.getCFunc().hasPointerArg();
126
127 if (!isVoid) {
128 out.println(iii +
129 jfunc.getType() + " _returnValue;");
130 }
131 out.println(iii +
132 (isVoid ? "" : "_returnValue = ") +
133 jfunc.getName() +
134 (isPointerFunc ? "Bounds" : "" ) +
135 "(");
Jack Palevich6cbca502009-04-13 16:22:25 -0700136
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800137 int numArgs = jfunc.getNumArgs();
138 for (int i = 0; i < numArgs; i++) {
139 String argName = jfunc.getArgName(i);
140 JType argType = jfunc.getArgType(i);
141
142 if (grabArray && argType.isTypedBuffer()) {
143 String typeName = argType.getBaseType();
144 typeName = typeName.substring(9, typeName.length() - 6);
145 out.println(iii + indent + "get" + typeName + "Array(" + argName + "),");
Jack Palevich6cbca502009-04-13 16:22:25 -0700146 out.print(iii + indent + "getOffset(" + argName + ")");
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800147 } else {
148 out.print(iii + indent + argName);
149 }
150 if (i == numArgs - 1) {
151 if (isPointerFunc) {
152 out.println(",");
153 out.println(iii + indent + argName + ".remaining()");
154 } else {
155 out.println();
156 }
157 } else {
158 out.println(",");
159 }
160 }
Jack Palevich6cbca502009-04-13 16:22:25 -0700161
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800162 out.println(iii + ");");
163 }
164
Jack Palevichffac1ef2009-04-14 19:00:09 -0700165 void printIfcheckPostamble(PrintStream out, boolean isBuffer, boolean emitExceptionCheck,
166 String iii) {
167 printIfcheckPostamble(out, isBuffer, emitExceptionCheck,
168 "offset", "_remaining", iii);
169 }
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800170
Jack Palevichffac1ef2009-04-14 19:00:09 -0700171 void printIfcheckPostamble(PrintStream out, boolean isBuffer, boolean emitExceptionCheck,
172 String offset, String remaining, String iii) {
173 out.println(iii + " default:");
174 out.println(iii + " _needed = 0;");
175 out.println(iii + " break;");
176 out.println(iii + "}");
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800177
Jack Palevichffac1ef2009-04-14 19:00:09 -0700178 out.println(iii + "if (" + remaining + " < _needed) {");
179 if (emitExceptionCheck) {
180 out.println(iii + indent + "_exception = 1;");
181 }
182 out.println(iii + indent +
183 (mUseCPlusPlus ? "_env" : "(*_env)") +
184 "->ThrowNew(" +
185 (mUseCPlusPlus ? "" : "_env, ") +
186 "IAEClass, " +
187 "\"" +
188 (isBuffer ?
189 "remaining()" : "length - " + offset) +
190 " < needed\");");
191 out.println(iii + indent + "goto exit;");
192 needsExit = true;
193 out.println(iii + "}");
194 }
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800195
196 boolean isNullAllowed(CFunc cfunc) {
197 String[] checks = mChecker.getChecks(cfunc.getName());
198 int index = 1;
199 if (checks != null) {
200 while (index < checks.length) {
201 if (checks[index].equals("return")) {
202 index += 2;
203 } else if (checks[index].startsWith("check")) {
204 index += 3;
205 } else if (checks[index].equals("ifcheck")) {
206 index += 5;
207 } else if (checks[index].equals("unsupported")) {
208 index += 1;
209 } else if (checks[index].equals("nullAllowed")) {
210 return true;
211 } else {
212 System.out.println("Error: unknown keyword \"" +
213 checks[index] + "\"");
214 System.exit(0);
215 }
216 }
217 }
218 return false;
219 }
220
221 String getErrorReturnValue(CFunc cfunc) {
222 CType returnType = cfunc.getType();
223 boolean isVoid = returnType.isVoid();
224 if (isVoid) {
225 return null;
226 }
227
228 String[] checks = mChecker.getChecks(cfunc.getName());
229
230 int index = 1;
231 if (checks != null) {
232 while (index < checks.length) {
233 if (checks[index].equals("return")) {
234 return checks[index + 1];
235 } else if (checks[index].startsWith("check")) {
236 index += 3;
237 } else if (checks[index].equals("ifcheck")) {
238 index += 5;
239 } else if (checks[index].equals("unsupported")) {
240 index += 1;
241 } else if (checks[index].equals("nullAllowed")) {
242 index += 1;
243 } else {
244 System.out.println("Error: unknown keyword \"" +
245 checks[index] + "\"");
246 System.exit(0);
247 }
248 }
249 }
250
251 return null;
252 }
253
254 boolean isUnsupportedFunc(CFunc cfunc) {
255 String[] checks = mChecker.getChecks(cfunc.getName());
256 int index = 1;
257 if (checks != null) {
258 while (index < checks.length) {
259 if (checks[index].equals("unsupported")) {
260 return true;
261 } else if (checks[index].equals("return")) {
262 index += 2;
263 } else if (checks[index].startsWith("check")) {
264 index += 3;
265 } else if (checks[index].equals("ifcheck")) {
266 index += 5;
267 } else if (checks[index].equals("nullAllowed")) {
268 index += 1;
269 } else {
270 System.out.println("Error: unknown keyword \"" +
271 checks[index] + "\"");
272 System.exit(0);
273 }
274 }
275 }
276 return false;
277 }
278
279 void emitNativeBoundsChecks(CFunc cfunc, String cname, PrintStream out,
Jack Palevichffac1ef2009-04-14 19:00:09 -0700280 boolean isBuffer, boolean emitExceptionCheck, String offset, String remaining, String iii) {
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800281
Jack Palevichffac1ef2009-04-14 19:00:09 -0700282 String[] checks = mChecker.getChecks(cfunc.getName());
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800283
Jack Palevichffac1ef2009-04-14 19:00:09 -0700284 boolean lastWasIfcheck = false;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800285
Jack Palevichffac1ef2009-04-14 19:00:09 -0700286 int index = 1;
287 if (checks != null) {
288 while (index < checks.length) {
289 if (checks[index].startsWith("check")) {
290 if (lastWasIfcheck) {
291 printIfcheckPostamble(out, isBuffer, emitExceptionCheck,
292 offset, remaining, iii);
293 }
294 lastWasIfcheck = false;
295 if (cname != null && !cname.equals(checks[index + 1])) {
296 index += 3;
297 continue;
298 }
299 out.println(iii + "if (" + remaining + " < " +
300 checks[index + 2] +
301 ") {");
302 if (emitExceptionCheck) {
303 out.println(iii + indent + "_exception = 1;");
304 }
305 String exceptionClassName = "IAEClass";
306 // If the "check" keyword was of the form
307 // "check_<class name>", use the class name in the
308 // exception to be thrown
309 int underscore = checks[index].indexOf('_');
310 if (underscore >= 0) {
311 exceptionClassName = checks[index].substring(underscore + 1) + "Class";
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800312 }
Jack Palevichffac1ef2009-04-14 19:00:09 -0700313 out.println(iii + indent +
314 (mUseCPlusPlus ? "_env" : "(*_env)") +
315 "->ThrowNew(" +
316 (mUseCPlusPlus ? "" : "_env, ") +
317 exceptionClassName + ", " +
318 "\"" +
319 (isBuffer ?
320 "remaining()" : "length - " + offset) +
321 " < " + checks[index + 2] +
322 "\");");
323
324 out.println(iii + indent + "goto exit;");
325 needsExit = true;
326 out.println(iii + "}");
327
328 index += 3;
329 } else if (checks[index].equals("ifcheck")) {
330 String[] matches = checks[index + 4].split(",");
331
332 if (!lastWasIfcheck) {
333 out.println(iii + "int _needed;");
334 out.println(iii +
335 "switch (" +
336 checks[index + 3] +
337 ") {");
338 }
339
340 for (int i = 0; i < matches.length; i++) {
341 out.println("#if defined(" + matches[i] + ")");
342 out.println(iii +
343 " case " +
344 matches[i] +
345 ":");
346 out.println("#endif // defined(" + matches[i] + ")");
347 }
348 out.println(iii +
349 " _needed = " +
350 checks[index + 2] +
351 ";");
352 out.println(iii +
353 " break;");
354
355 lastWasIfcheck = true;
356 index += 5;
357 } else if (checks[index].equals("return")) {
358 // ignore
359 index += 2;
360 } else if (checks[index].equals("unsupported")) {
361 // ignore
362 index += 1;
363 } else if (checks[index].equals("nullAllowed")) {
364 // ignore
365 index += 1;
366 } else {
367 System.out.println("Error: unknown keyword \"" +
368 checks[index] + "\"");
369 System.exit(0);
370 }
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800371 }
Jack Palevichffac1ef2009-04-14 19:00:09 -0700372 }
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800373
Jack Palevichffac1ef2009-04-14 19:00:09 -0700374 if (lastWasIfcheck) {
375 printIfcheckPostamble(out, isBuffer, emitExceptionCheck, iii);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800376 }
377 }
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800378
Jack Palevichffac1ef2009-04-14 19:00:09 -0700379 boolean hasNonConstArg(JFunc jfunc, CFunc cfunc, List<Integer> nonPrimitiveArgs) {
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800380 if (nonPrimitiveArgs.size() > 0) {
381 for (int i = nonPrimitiveArgs.size() - 1; i >= 0; i--) {
382 int idx = nonPrimitiveArgs.get(i).intValue();
383 int cIndex = jfunc.getArgCIndex(idx);
384 if (jfunc.getArgType(idx).isArray()) {
385 if (!cfunc.getArgType(cIndex).isConst()) {
386 return true;
387 }
388 } else if (jfunc.getArgType(idx).isBuffer()) {
389 if (!cfunc.getArgType(cIndex).isConst()) {
390 return true;
391 }
392 }
393 }
394 }
395
396 return false;
397 }
Jack Palevich6cbca502009-04-13 16:22:25 -0700398
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800399 /**
400 * Emit a function in several variants:
401 *
402 * if nativeDecl: public native <returntype> func(args);
403 *
404 * if !nativeDecl:
405 * if interfaceDecl: public <returntype> func(args);
406 * if !interfaceDecl: public <returntype> func(args) { body }
407 */
Jack Palevichffac1ef2009-04-14 19:00:09 -0700408 void emitFunction(JFunc jfunc, PrintStream out, boolean nativeDecl, boolean interfaceDecl) {
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800409 boolean isPointerFunc =
410 jfunc.getName().endsWith("Pointer") &&
411 jfunc.getCFunc().hasPointerArg();
412
413 if (!nativeDecl && !interfaceDecl && !isPointerFunc) {
414 // If it's not a pointer function, we've already emitted it
415 // with nativeDecl == true
416 return;
417 }
418
Jack Palevich427f5852009-04-15 19:13:17 -0700419 String maybeStatic = mUseStaticMethods ? "static " : "";
420
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800421 if (isPointerFunc) {
422 out.println(indent +
Jack Palevich427f5852009-04-15 19:13:17 -0700423 (nativeDecl ? "private " + maybeStatic +"native " :
424 (interfaceDecl ? "" : "public ") + maybeStatic) +
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800425 jfunc.getType() + " " +
426 jfunc.getName() +
427 (nativeDecl ? "Bounds" : "") +
428 "(");
429 } else {
430 out.println(indent +
Jack Palevich427f5852009-04-15 19:13:17 -0700431 (nativeDecl ? "public " + maybeStatic +"native " :
432 (interfaceDecl ? "" : "public ") + maybeStatic) +
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800433 jfunc.getType() + " " +
434 jfunc.getName() +
435 "(");
436 }
Jack Palevich6cbca502009-04-13 16:22:25 -0700437
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800438 int numArgs = jfunc.getNumArgs();
439 for (int i = 0; i < numArgs; i++) {
440 String argName = jfunc.getArgName(i);
441 JType argType = jfunc.getArgType(i);
Jack Palevich6cbca502009-04-13 16:22:25 -0700442
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800443 out.print(indent + indent + argType + " " + argName);
444 if (i == numArgs - 1) {
445 if (isPointerFunc && nativeDecl) {
446 out.println(",");
447 out.println(indent + indent + "int remaining");
448 } else {
449 out.println();
450 }
451 } else {
452 out.println(",");
453 }
454 }
455
456 if (nativeDecl || interfaceDecl) {
457 out.println(indent + ");");
458 } else {
459 out.println(indent + ") {");
460
461 String iii = indent + indent;
462
Jack Palevich46d25a32009-05-07 18:28:29 -0700463 // emitBoundsChecks(jfunc, out, iii);
464 emitFunctionCall(jfunc, out, iii, false);
465
466 // Set the pointer after we call the native code, so that if
467 // the native code throws an exception we don't modify the
468 // pointer. We assume that the native code is written so that
469 // if an exception is thrown, then the underlying glXXXPointer
470 // function will not have been called.
471
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800472 String fname = jfunc.getName();
473 if (isPointerFunc) {
474 // TODO - deal with VBO variants
475 if (fname.equals("glColorPointer")) {
476 out.println(iii + "if ((size == 4) &&");
477 out.println(iii + " ((type == GL_FLOAT) ||");
478 out.println(iii + " (type == GL_UNSIGNED_BYTE) ||");
479 out.println(iii + " (type == GL_FIXED)) &&");
480 out.println(iii + " (stride >= 0)) {");
481 out.println(iii + indent + "_colorPointer = pointer;");
482 out.println(iii + "}");
483 } else if (fname.equals("glNormalPointer")) {
484 out.println(iii + "if (((type == GL_FLOAT) ||");
485 out.println(iii + " (type == GL_BYTE) ||");
486 out.println(iii + " (type == GL_SHORT) ||");
487 out.println(iii + " (type == GL_FIXED)) &&");
488 out.println(iii + " (stride >= 0)) {");
489 out.println(iii + indent + "_normalPointer = pointer;");
490 out.println(iii + "}");
491 } else if (fname.equals("glTexCoordPointer")) {
492 out.println(iii + "if (((size == 2) ||");
493 out.println(iii + " (size == 3) ||");
494 out.println(iii + " (size == 4)) &&");
495 out.println(iii + " ((type == GL_FLOAT) ||");
496 out.println(iii + " (type == GL_BYTE) ||");
497 out.println(iii + " (type == GL_SHORT) ||");
498 out.println(iii + " (type == GL_FIXED)) &&");
499 out.println(iii + " (stride >= 0)) {");
500 out.println(iii + indent + "_texCoordPointer = pointer;");
501 out.println(iii + "}");
502 } else if (fname.equals("glVertexPointer")) {
503 out.println(iii + "if (((size == 2) ||");
504 out.println(iii + " (size == 3) ||");
505 out.println(iii + " (size == 4)) &&");
506 out.println(iii + " ((type == GL_FLOAT) ||");
507 out.println(iii + " (type == GL_BYTE) ||");
508 out.println(iii + " (type == GL_SHORT) ||");
509 out.println(iii + " (type == GL_FIXED)) &&");
510 out.println(iii + " (stride >= 0)) {");
511 out.println(iii + indent + "_vertexPointer = pointer;");
512 out.println(iii + "}");
513 }
514 }
515
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800516 boolean isVoid = jfunc.getType().isVoid();
517
518 if (!isVoid) {
519 out.println(indent + indent + "return _returnValue;");
520 }
521 out.println(indent + "}");
522 }
523 out.println();
524 }
525
Jack Palevichffac1ef2009-04-14 19:00:09 -0700526 public void addNativeRegistration(String s) {
527 nativeRegistrations.add(s);
528 }
529
Jack Palevich427f5852009-04-15 19:13:17 -0700530 public void emitNativeRegistration(String registrationFunctionName,
531 PrintStream cStream) {
Jack Palevichffac1ef2009-04-14 19:00:09 -0700532 cStream.println("static const char *classPathName = \"" +
533 mClassPathName +
534 "\";");
535 cStream.println();
536
537 cStream.println("static JNINativeMethod methods[] = {");
538
539 cStream.println("{\"_nativeClassInit\", \"()V\", (void*)nativeClassInit },");
540
541 Iterator<String> i = nativeRegistrations.iterator();
542 while (i.hasNext()) {
543 cStream.println(i.next());
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800544 }
Jack Palevich6cbca502009-04-13 16:22:25 -0700545
Jack Palevichffac1ef2009-04-14 19:00:09 -0700546 cStream.println("};");
547 cStream.println();
548
549
Jack Palevich427f5852009-04-15 19:13:17 -0700550 cStream.println("int " + registrationFunctionName + "(JNIEnv *_env)");
Jack Palevichffac1ef2009-04-14 19:00:09 -0700551 cStream.println("{");
552 cStream.println(indent +
553 "int err;");
554
555 cStream.println(indent +
556 "err = android::AndroidRuntime::registerNativeMethods(_env, classPathName, methods, NELEM(methods));");
557
558 cStream.println(indent + "return err;");
559 cStream.println("}");
560 }
561
562 public JniCodeEmitter() {
563 super();
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800564 }
565
566 String getJniType(JType jType) {
567 if (jType.isVoid()) {
568 return "void";
569 }
570
571 String baseType = jType.getBaseType();
572 if (jType.isPrimitive()) {
573 if (baseType.equals("String")) {
574 return "jstring";
575 } else {
576 return "j" + baseType;
577 }
578 } else if (jType.isArray()) {
579 return "j" + baseType + "Array";
580 } else {
581 return "jobject";
582 }
583 }
Jack Palevich6cbca502009-04-13 16:22:25 -0700584
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800585 String getJniMangledName(String name) {
586 name = name.replaceAll("_", "_1");
587 name = name.replaceAll(";", "_2");
588 name = name.replaceAll("\\[", "_3");
589 return name;
590 }
591
592 public void emitJniCode(JFunc jfunc, PrintStream out) {
593 CFunc cfunc = jfunc.getCFunc();
Jack Palevich6cbca502009-04-13 16:22:25 -0700594
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800595 // Emit comment identifying original C function
596 //
597 // Example:
598 //
599 // /* void glClipPlanef ( GLenum plane, const GLfloat *equation ) */
600 //
601 out.println("/* " + cfunc.getOriginal() + " */");
602
603 // Emit JNI signature (name)
604 //
605 // Example:
606 //
607 // void
608 // android_glClipPlanef__I_3FI
609 //
610
611 String outName = "android_" + jfunc.getName();
612 boolean isPointerFunc = outName.endsWith("Pointer") &&
613 jfunc.getCFunc().hasPointerArg();
614 boolean isVBOPointerFunc = (outName.endsWith("Pointer") ||
615 outName.endsWith("DrawElements")) &&
616 !jfunc.getCFunc().hasPointerArg();
617 if (isPointerFunc) {
618 outName += "Bounds";
619 }
620
621 out.print("static ");
622 out.println(getJniType(jfunc.getType()));
623 out.print(outName);
624
625 String rsignature = getJniName(jfunc.getType());
626
627 String signature = "";
628 int numArgs = jfunc.getNumArgs();
629 for (int i = 0; i < numArgs; i++) {
630 JType argType = jfunc.getArgType(i);
631 signature += getJniName(argType);
632 }
633 if (isPointerFunc) {
634 signature += "I";
635 }
636
637 // Append signature to function name
Jack Palevich50d0b142009-11-19 16:34:55 +0800638 String sig = getJniMangledName(signature).replace('.', '_').replace('/', '_');
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800639 out.print("__" + sig);
640 outName += "__" + sig;
Jack Palevich6cbca502009-04-13 16:22:25 -0700641
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800642 signature = signature.replace('.', '/');
643 rsignature = rsignature.replace('.', '/');
Jack Palevich6cbca502009-04-13 16:22:25 -0700644
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800645 out.println();
646 if (rsignature.length() == 0) {
647 rsignature = "V";
648 }
649
650 String s = "{\"" +
651 jfunc.getName() +
652 (isPointerFunc ? "Bounds" : "") +
653 "\", \"(" + signature +")" +
654 rsignature +
655 "\", (void *) " +
656 outName +
657 " },";
658 nativeRegistrations.add(s);
659
660 List<Integer> nonPrimitiveArgs = new ArrayList<Integer>();
Jack Palevich50d0b142009-11-19 16:34:55 +0800661 List<Integer> stringArgs = new ArrayList<Integer>();
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800662 int numBufferArgs = 0;
663 List<String> bufferArgNames = new ArrayList<String>();
664
665 // Emit JNI signature (arguments)
666 //
667 // Example:
668 //
669 // (JNIEnv *_env, jobject this, jint plane, jfloatArray equation_ref, jint offset) {
670 //
671 out.print(" (JNIEnv *_env, jobject _this");
672 for (int i = 0; i < numArgs; i++) {
673 out.print(", ");
674 JType argType = jfunc.getArgType(i);
675 String suffix;
676 if (!argType.isPrimitive()) {
677 if (argType.isArray()) {
678 suffix = "_ref";
679 } else {
680 suffix = "_buf";
681 }
682 nonPrimitiveArgs.add(new Integer(i));
683 if (jfunc.getArgType(i).isBuffer()) {
684 int cIndex = jfunc.getArgCIndex(i);
685 String cname = cfunc.getArgName(cIndex);
686 bufferArgNames.add(cname);
687 numBufferArgs++;
688 }
689 } else {
690 suffix = "";
691 }
Jack Palevich50d0b142009-11-19 16:34:55 +0800692 if (argType.isString()) {
693 stringArgs.add(new Integer(i));
694 }
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800695
696 out.print(getJniType(argType) + " " + jfunc.getArgName(i) + suffix);
697 }
698 if (isPointerFunc) {
699 out.print(", jint remaining");
700 }
701 out.println(") {");
Jack Palevich6cbca502009-04-13 16:22:25 -0700702
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800703 int numArrays = 0;
704 int numBuffers = 0;
Jack Palevich50d0b142009-11-19 16:34:55 +0800705 int numStrings = 0;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800706 for (int i = 0; i < nonPrimitiveArgs.size(); i++) {
707 int idx = nonPrimitiveArgs.get(i).intValue();
Jack Palevich50d0b142009-11-19 16:34:55 +0800708 JType argType = jfunc.getArgType(idx);
709 if (argType.isArray()) {
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800710 ++numArrays;
711 }
Jack Palevich50d0b142009-11-19 16:34:55 +0800712 if (argType.isBuffer()) {
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800713 ++numBuffers;
714 }
Jack Palevich50d0b142009-11-19 16:34:55 +0800715 if (argType.isString()) {
716 ++numStrings;
717 }
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800718 }
719
720 // Emit method body
721
722 // Emit local variable declarations for _exception and _returnValue
723 //
724 // Example:
725 //
726 // android::gl::ogles_context_t *ctx;
Jack Palevich6cbca502009-04-13 16:22:25 -0700727 //
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800728 // jint _exception;
729 // GLenum _returnValue;
730 //
731 CType returnType = cfunc.getType();
732 boolean isVoid = returnType.isVoid();
733
734 boolean isUnsupported = isUnsupportedFunc(cfunc);
735 if (isUnsupported) {
736 out.println(indent +
737 "_env->ThrowNew(UOEClass,");
738 out.println(indent +
739 " \"" + cfunc.getName() + "\");");
740 if (!isVoid) {
741 String retval = getErrorReturnValue(cfunc);
742 out.println(indent + "return " + retval + ";");
743 }
744 out.println("}");
745 out.println();
746 return;
747 }
748
749 if (mUseContextPointer) {
750 out.println(indent +
751 "android::gl::ogles_context_t *ctx = getContext(_env, _this);");
752 }
753
Jack Palevich50d0b142009-11-19 16:34:55 +0800754 boolean initializeReturnValue = stringArgs.size() > 0;
755
756 boolean emitExceptionCheck = (numArrays > 0 || numBuffers > 0 || numStrings > 0) &&
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800757 hasNonConstArg(jfunc, cfunc, nonPrimitiveArgs);
758 // mChecker.getChecks(cfunc.getName()) != null
759
760 // Emit an _exeption variable if there will be error checks
761 if (emitExceptionCheck) {
762 out.println(indent + "jint _exception = 0;");
763 }
764
765 // Emit a single _array or multiple _XXXArray variables
766 if (numBufferArgs == 1) {
767 out.println(indent + "jarray _array = (jarray) 0;");
768 } else {
769 for (int i = 0; i < numBufferArgs; i++) {
770 out.println(indent + "jarray _" + bufferArgNames.get(i) +
771 "Array = (jarray) 0;");
772 }
773 }
774 if (!isVoid) {
775 String retval = getErrorReturnValue(cfunc);
776 if (retval != null) {
777 out.println(indent + returnType.getDeclaration() +
778 " _returnValue = " + retval + ";");
Jack Palevich50d0b142009-11-19 16:34:55 +0800779 } else if (initializeReturnValue) {
780 out.println(indent + returnType.getDeclaration() +
781 " _returnValue = 0;");
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800782 } else {
783 out.println(indent + returnType.getDeclaration() +
784 " _returnValue;");
785 }
786 }
787
788 // Emit local variable declarations for pointer arguments
789 //
790 // Example:
791 //
792 // GLfixed *eqn_base;
793 // GLfixed *eqn;
794 //
795 String offset = "offset";
796 String remaining = "_remaining";
797 if (nonPrimitiveArgs.size() > 0) {
798 for (int i = 0; i < nonPrimitiveArgs.size(); i++) {
799 int idx = nonPrimitiveArgs.get(i).intValue();
800 int cIndex = jfunc.getArgCIndex(idx);
801 String cname = cfunc.getArgName(cIndex);
802
803 CType type = cfunc.getArgType(jfunc.getArgCIndex(idx));
804 String decl = type.getDeclaration();
805 if (jfunc.getArgType(idx).isArray()) {
806 out.println(indent +
807 decl +
808 (decl.endsWith("*") ? "" : " ") +
809 jfunc.getArgName(idx) +
810 "_base = (" + decl + ") 0;");
811 }
Jack Palevich50d0b142009-11-19 16:34:55 +0800812 remaining = ((numArrays + numBuffers) <= 1) ? "_remaining" :
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800813 "_" + cname + "Remaining";
814 out.println(indent +
815 "jint " + remaining + ";");
816 out.println(indent +
817 decl +
818 (decl.endsWith("*") ? "" : " ") +
Jack Palevich6cbca502009-04-13 16:22:25 -0700819 jfunc.getArgName(idx) +
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800820 " = (" + decl + ") 0;");
821 }
822
823 out.println();
824 }
825
Jack Palevich50d0b142009-11-19 16:34:55 +0800826 // Emit local variable declaration for strings
827 if (stringArgs.size() > 0) {
828 for (int i = 0; i < stringArgs.size(); i++) {
829 int idx = stringArgs.get(i).intValue();
830 int cIndex = jfunc.getArgCIndex(idx);
831 String cname = cfunc.getArgName(cIndex);
832
833 out.println(indent + "const char* _native" + cname + " = 0;");
834 }
835
836 out.println();
837 }
838
839 // Null pointer checks and GetStringUTFChars
840 if (stringArgs.size() > 0) {
841 for (int i = 0; i < stringArgs.size(); i++) {
842 int idx = stringArgs.get(i).intValue();
843 int cIndex = jfunc.getArgCIndex(idx);
844 String cname = cfunc.getArgName(cIndex);
845
846 CType type = cfunc.getArgType(jfunc.getArgCIndex(idx));
847 String decl = type.getDeclaration();
848 out.println(indent + "if (!" + cname + ") {");
849 out.println(indent + " _env->ThrowNew(IAEClass, \"" + cname + " == null\");");
850 out.println(indent + " goto exit;");
851 needsExit = true;
852 out.println(indent + "}");
853
854 out.println(indent + "_native" + cname + " = _env->GetStringUTFChars(" + cname + ", 0);");
855 }
856
857 out.println();
858 }
859
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800860 // Emit 'GetPrimitiveArrayCritical' for arrays
861 // Emit 'GetPointer' calls for Buffer pointers
862 int bufArgIdx = 0;
863 if (nonPrimitiveArgs.size() > 0) {
864 for (int i = 0; i < nonPrimitiveArgs.size(); i++) {
865 int idx = nonPrimitiveArgs.get(i).intValue();
866 int cIndex = jfunc.getArgCIndex(idx);
Jack Palevich6cbca502009-04-13 16:22:25 -0700867
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800868 String cname = cfunc.getArgName(cIndex);
869 offset = numArrays <= 1 ? "offset" :
870 cname + "Offset";
Jack Palevich50d0b142009-11-19 16:34:55 +0800871 remaining = ((numArrays + numBuffers) <= 1) ? "_remaining" :
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800872 "_" + cname + "Remaining";
873
874 if (jfunc.getArgType(idx).isArray()) {
875 out.println(indent +
Jack Palevich6cbca502009-04-13 16:22:25 -0700876 "if (!" +
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800877 cname +
878 "_ref) {");
879 if (emitExceptionCheck) {
880 out.println(indent + indent + "_exception = 1;");
881 }
882 out.println(indent + " " +
883 (mUseCPlusPlus ? "_env" : "(*_env)") +
884 "->ThrowNew(" +
885 (mUseCPlusPlus ? "" : "_env, ") +
886 "IAEClass, " +
887 "\"" + cname +
888 " == null\");");
889 out.println(indent + " goto exit;");
890 needsExit = true;
891 out.println(indent + "}");
892
893 out.println(indent + "if (" + offset + " < 0) {");
894 if (emitExceptionCheck) {
895 out.println(indent + indent + "_exception = 1;");
896 }
897 out.println(indent + " " +
898 (mUseCPlusPlus ? "_env" : "(*_env)") +
899 "->ThrowNew(" +
900 (mUseCPlusPlus ? "" : "_env, ") +
901 "IAEClass, " +
902 "\"" + offset + " < 0\");");
903 out.println(indent + " goto exit;");
904 needsExit = true;
905 out.println(indent + "}");
906
907 out.println(indent + remaining + " = " +
Jack Palevich6cbca502009-04-13 16:22:25 -0700908 (mUseCPlusPlus ? "_env" : "(*_env)") +
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800909 "->GetArrayLength(" +
910 (mUseCPlusPlus ? "" : "_env, ") +
911 cname + "_ref) - " + offset + ";");
912
913 emitNativeBoundsChecks(cfunc, cname, out, false,
914 emitExceptionCheck,
915 offset, remaining, " ");
916
917 out.println(indent +
918 cname +
919 "_base = (" +
920 cfunc.getArgType(cIndex).getDeclaration() +
921 ")");
922 out.println(indent + " " +
923 (mUseCPlusPlus ? "_env" : "(*_env)") +
924 "->GetPrimitiveArrayCritical(" +
Jack Palevich6cbca502009-04-13 16:22:25 -0700925 (mUseCPlusPlus ? "" : "_env, ") +
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800926 jfunc.getArgName(idx) +
927 "_ref, (jboolean *)0);");
928 out.println(indent +
929 cname + " = " + cname + "_base + " + offset +
930 ";");
931 out.println();
932 } else {
933 String array = numBufferArgs <= 1 ? "_array" :
934 "_" + bufferArgNames.get(bufArgIdx++) + "Array";
935
Jack Palevich46d25a32009-05-07 18:28:29 -0700936 boolean nullAllowed = isNullAllowed(cfunc) || isPointerFunc;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800937 if (nullAllowed) {
938 out.println(indent + "if (" + cname + "_buf) {");
939 out.print(indent);
940 }
Jack Palevich6cbca502009-04-13 16:22:25 -0700941
Jack Palevich46d25a32009-05-07 18:28:29 -0700942 if (isPointerFunc) {
943 out.println(indent +
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800944 cname +
945 " = (" +
946 cfunc.getArgType(cIndex).getDeclaration() +
Jack Palevich6eedc8d2009-05-15 18:13:34 -0700947 ") getDirectBufferPointer(_env, " +
Jack Palevich46d25a32009-05-07 18:28:29 -0700948 cname + "_buf);");
949 String iii = " ";
Jack Palevich5afdc872009-10-21 11:02:44 -0700950 out.println(iii + indent + "if ( ! " + cname + " ) {");
Jack Palevich46d25a32009-05-07 18:28:29 -0700951 out.println(iii + iii + indent + "return;");
952 out.println(iii + indent + "}");
953 } else {
954 out.println(indent +
955 cname +
956 " = (" +
957 cfunc.getArgType(cIndex).getDeclaration() +
958 ")getPointer(_env, " +
959 cname +
960 "_buf, &" + array + ", &" + remaining +
961 ");");
962 }
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800963
Jack Palevich5afdc872009-10-21 11:02:44 -0700964 emitNativeBoundsChecks(cfunc, cname, out, true,
965 emitExceptionCheck,
966 offset, remaining, nullAllowed ? " " : " ");
967
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800968 if (nullAllowed) {
969 out.println(indent + "}");
970 }
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800971 }
972 }
973 }
974
975 if (!isVoid) {
976 out.print(indent + "_returnValue = ");
977 } else {
978 out.print(indent);
979 }
980 String name = cfunc.getName();
981
982 if (mUseContextPointer) {
983 name = name.substring(2, name.length()); // Strip off 'gl' prefix
984 name = name.substring(0, 1).toLowerCase() +
985 name.substring(1, name.length());
986 out.print("ctx->procs.");
987 }
Jack Palevich6cbca502009-04-13 16:22:25 -0700988
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800989 out.print(name + (isPointerFunc ? "Bounds" : "") + "(");
990
Jack Palevich6cbca502009-04-13 16:22:25 -0700991 numArgs = cfunc.getNumArgs();
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800992 if (numArgs == 0) {
993 if (mUseContextPointer) {
994 out.println("ctx);");
995 } else {
996 out.println(");");
997 }
998 } else {
999 if (mUseContextPointer) {
1000 out.println("ctx,");
1001 } else {
1002 out.println();
1003 }
1004 for (int i = 0; i < numArgs; i++) {
1005 String typecast;
1006 if (i == numArgs - 1 && isVBOPointerFunc) {
1007 typecast = "const GLvoid *";
1008 } else {
1009 typecast = cfunc.getArgType(i).getDeclaration();
1010 }
1011 out.print(indent + indent +
1012 "(" +
1013 typecast +
Jack Palevich50d0b142009-11-19 16:34:55 +08001014 ")");
1015 if (cfunc.getArgType(i).isConstCharPointer()) {
1016 out.print("_native");
1017 }
1018 out.print(cfunc.getArgName(i));
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -08001019
1020 if (i == numArgs - 1) {
1021 if (isPointerFunc) {
1022 out.println(",");
1023 out.println(indent + indent + "(GLsizei)remaining");
1024 } else {
1025 out.println();
1026 }
1027 } else {
1028 out.println(",");
1029 }
1030 }
1031 out.println(indent + ");");
1032 }
1033
1034 if (needsExit) {
1035 out.println();
1036 out.println("exit:");
1037 needsExit = false;
1038 }
1039
1040 bufArgIdx = 0;
1041 if (nonPrimitiveArgs.size() > 0) {
1042 for (int i = nonPrimitiveArgs.size() - 1; i >= 0; i--) {
1043 int idx = nonPrimitiveArgs.get(i).intValue();
1044
1045 int cIndex = jfunc.getArgCIndex(idx);
1046 if (jfunc.getArgType(idx).isArray()) {
Jack Palevich6cbca502009-04-13 16:22:25 -07001047
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -08001048 // If the argument is 'const', GL will not write to it.
1049 // In this case, we can use the 'JNI_ABORT' flag to avoid
1050 // the need to write back to the Java array
1051 out.println(indent +
1052 "if (" + jfunc.getArgName(idx) + "_base) {");
1053 out.println(indent + indent +
1054 (mUseCPlusPlus ? "_env" : "(*_env)") +
1055 "->ReleasePrimitiveArrayCritical(" +
Jack Palevich6cbca502009-04-13 16:22:25 -07001056 (mUseCPlusPlus ? "" : "_env, ") +
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -08001057 jfunc.getArgName(idx) + "_ref, " +
1058 cfunc.getArgName(cIndex) +
1059 "_base,");
1060 out.println(indent + indent + indent +
1061 (cfunc.getArgType(cIndex).isConst() ?
1062 "JNI_ABORT" :
1063 "_exception ? JNI_ABORT: 0") +
1064 ");");
1065 out.println(indent + "}");
1066 } else if (jfunc.getArgType(idx).isBuffer()) {
Jack Palevich46d25a32009-05-07 18:28:29 -07001067 if (! isPointerFunc) {
1068 String array = numBufferArgs <= 1 ? "_array" :
1069 "_" + bufferArgNames.get(bufArgIdx++) + "Array";
1070 out.println(indent + "if (" + array + ") {");
1071 out.println(indent + indent +
1072 "releasePointer(_env, " + array + ", " +
1073 cfunc.getArgName(cIndex) +
1074 ", " +
1075 (cfunc.getArgType(cIndex).isConst() ?
1076 "JNI_FALSE" : "_exception ? JNI_FALSE :" +
1077 " JNI_TRUE") +
1078 ");");
1079 out.println(indent + "}");
1080 }
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -08001081 }
1082 }
1083 }
1084
Jack Palevich50d0b142009-11-19 16:34:55 +08001085 // Emit local variable declaration for strings
1086 if (stringArgs.size() > 0) {
1087 for (int i = 0; i < stringArgs.size(); i++) {
1088 int idx = stringArgs.get(i).intValue();
1089 int cIndex = jfunc.getArgCIndex(idx);
1090 String cname = cfunc.getArgName(cIndex);
1091
1092 out.println(indent + "if (_native" + cname + ") {");
1093 out.println(indent + " _env->ReleaseStringUTFChars(" + cname + ", _native" + cname + ");");
1094 out.println(indent + "}");
1095 }
1096
1097 out.println();
1098 }
1099
1100
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -08001101 if (!isVoid) {
1102 out.println(indent + "return _returnValue;");
1103 }
1104
1105 out.println("}");
1106 out.println();
1107 }
1108
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -08001109}