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