1 /++ 2 Provides easy interoperability with Java code through JNI. 3 4 Given this Java: 5 ```java 6 class Hello { 7 public native void hi(String s); 8 public native String stringFromJNI(); 9 public native String returnNull(); 10 public native void throwException(); 11 static { 12 System.loadLibrary("myjni"); 13 } 14 public static void main(String[] args) { 15 System.out.println("Hello from Java!"); 16 Hello h = new Hello(); 17 // we can pass data back and forth normally 18 h.hi("jni"); 19 System.out.println(h.stringFromJNI()); 20 System.out.println(h.returnNull()); // it can handle null too 21 // and even forward exceptions (sort of, it puts it in a RuntimeException right now) 22 h.throwException(); 23 } 24 } 25 ``` 26 27 And this D: 28 --- 29 import arsd.jni; 30 31 // if it was in a Java package, you'd pass that 32 // in the string here instead of "". 33 final class Hello : JavaClass!("", Hello) { 34 35 @Export string stringFromJNI() { 36 return "hey, D returned this"; 37 } 38 39 @Export string returnNull() { 40 return null; 41 } 42 43 @Export void throwException() { 44 throw new Exception("exception from D"); 45 } 46 47 @Export void hi(string name) { 48 import std.stdio; 49 writefln("hello from D, %s", name); 50 } 51 } 52 --- 53 54 We can: 55 $(CONSOLE 56 $ javac Hello.java 57 $ dmd -shared myjni.d jni.d # compile into a shared lib 58 $ LD_LIBRARY_PATH=. java Hello 59 Hello from Java! 60 hello from D, jni 61 hey, D returned this 62 null 63 Exception in thread "main" java.lang.RuntimeException: object.Exception@myjni.d(14): exception from D 64 ---------------- 65 ??:? void myjni.Hello.throwException() [0x7f51d86dc17b] 66 ??:? Java_Hello_throwException [0x7f51d86dd3e0] 67 ??:? [0x7f51dd018406] 68 ??:? [0x7f51dd007ffc] 69 ??:? [0x7f51dd0004e6] 70 ??:? [0x7f51f16b0709] 71 ??:? [0x7f51f16c1339] 72 ??:? [0x7f51f16d208d] 73 ??:? [0x7f51f1f97058] 74 ??:? [0x7f51f1fae06a] 75 at Hello.throwException(Native Method) 76 at Hello.main(Hello.java:17) 77 ) 78 79 Exact details subject to change, especially of how I pass the exceptions over. 80 81 It is also possible to call Java methods and create Java objects from D with the `@Import` uda. 82 83 84 While you can write pretty ordinary looking D code, there's some things to keep in mind for safety and efficiency. 85 86 $(WARNING 87 ALL references passed to you through Java, including 88 arrays, objects, and even the `this` pointer, MUST NOT 89 be stored outside the lifetime of the immediate function 90 they were passed to! 91 92 You may be able to get the D compiler to help you with 93 this with the scope attribute, but regardless, don't 94 do it. 95 ) 96 97 It is YOUR responsibility to make sure parameter and return types 98 match between D and Java. The library will `static assert` given 99 unrepresentable types, but it cannot see what Java actually expects. 100 Getting this wrong can lead to memory corruption and crashes. 101 102 $(TIP 103 When possible, use `wstring` instead of `string` when 104 working with Java APIs. `wstring` matches the format 105 of Java's `String` so it avoids a conversion step. 106 ) 107 108 All [JavaClass] sub-objects should be marked `final` on the D 109 side. Java may subclass them, but D can't (at least not now). 110 111 Do not use default arguments on the exported methods. No promise 112 the wrapper will do what you want when called from Java. 113 114 You may choose to only import JavaClass from here to minimize the 115 namespace pollution. 116 117 Constructing Java objects works and it will pin it. Just remember 118 that `this` inside a method is still subject to escaping restrictions! 119 120 +/ 121 module arsd.jni; 122 123 // I need to figure out some way that users can set this. maybe. or dynamically fall back from newest to oldest we can handle 124 __gshared auto JNI_VERSION_DESIRED = JNI_VERSION_1_6; 125 126 // i could perhaps do a struct to bean thingy 127 128 /* 129 New Java classes: 130 131 class Foo : extends!Bar { 132 133 mixin stuff; 134 } 135 mixin stuff; 136 137 The `extends` template creates a wrapper that calls the nonvirtual 138 methods, so `super()` just works. 139 140 receiving an object should perhaps always give a subclass that is javafied; 141 calls the virtuals, unless of course it is final. 142 143 dynamic downcasts of java objects will probably never work. 144 */ 145 146 /+ 147 For interfaces: 148 149 Java interfaces should just inherit from IJavaObject. Otherwise they 150 work as normal in D. The final class is responsible for setting @Import 151 and @Export on the methods and declaring they are implemented. 152 153 Note that you can define D interfaces as well, that are not necessarily 154 known to Java. If your interface uses IJavaObject though, it assumes 155 that there is some kind of relationship. (mismatching this is not 156 necessarily fatal, but may cause runtime exceptions or compile errors.) 157 158 For parent classes: 159 160 The CRTP limits this. May switch to mixin template... but right now 161 the third argument to JavaClass declares the parent. It will alias this 162 to a thing that returns the casted (well, realistically, reconstructed) version. 163 +/ 164 165 /+ 166 FIXME: D lambdas might be automagically wrapped in a Java class... will 167 need to know what parent class Java expects and which method to override. 168 +/ 169 170 // FIXME: if user defines an interface with the appropriate RAII return values, 171 // it should let them do that for more efficiency 172 // e.g. @Import Manual!(int[]) getJavaArray(); 173 174 /+ 175 So in Java, a lambda expression is turned into an anonymous class 176 that implements the one abstract method in the required interface. 177 178 In D, they are a different type. And with no implicit construction I 179 can't convert automatically. 180 181 But I could prolly do something like javaLambda!Interface(x => foo) 182 but woof that isn't so much different than an anonymous class anymore. 183 +/ 184 185 /// hack used by the translator for default constructors not really being a default constructor 186 struct Default {} 187 188 /+ 189 final class CharSequence : JavaClass!("java.lang", CharSequence) { 190 @Import string toString(); // this triggers a dmd segfault! whoa. FIXME dmd 191 } 192 +/ 193 194 /++ 195 Java's String class implements its CharSequence interface. D's 196 string is not a class at all, so it cannot directly do that. Instead, 197 this translation of the interface has static methods to return a dummy 198 class wrapping D's string. 199 +/ 200 interface CharSequence : JavaInterface!("java.lang", CharSequence) { 201 /// 202 static CharSequence fromDString(string data) { 203 auto env = activeEnv; 204 assert(env !is null); 205 206 wchar[1024] buffer; 207 const(wchar)[] translated; 208 if(data.length < 1024) { 209 size_t len; 210 foreach(wchar ch; data) 211 buffer[len++] = ch; 212 translated = buffer[0 .. len]; 213 } else { 214 import std.conv; 215 translated = to!wstring(data); 216 } 217 // Java copies the buffer so it is perfectly fine to return here now 218 return dummyClass!(typeof(this))((*env).NewString(env, translated.ptr, cast(jsize) translated.length)); 219 } 220 /// 221 static CharSequence fromDString(wstring data) { 222 auto env = activeEnv; 223 assert(env !is null); 224 return dummyClass!(typeof(this))((*env).NewString(env, data.ptr, cast(jsize) data.length)); 225 } 226 } 227 228 /++ 229 Indicates that your interface represents an interface in Java. 230 231 Use this on the declaration, then your other classes can implement 232 it fairly normally (just with the @Import and @Export annotations added 233 in the appropriate places). D will require something be filled in on the 234 child classes so be sure to copy the @Import declarations there. 235 236 --- 237 interface IFoo : JavaInterface!("com.example", IFoo) { 238 string whatever(); 239 } 240 241 final class Foo : IFoo, JavaClass!("com.example", Foo) { 242 // need to tell D that the implementation exists, just in Java. 243 // (This actually generates the D implementation that just forwards to the existing java method) 244 @Import string whatever(); 245 } 246 --- 247 +/ 248 interface JavaInterface(string javaPackage, CRTP) : IJavaObject { 249 mixin JavaPackageId!(javaPackage, CRTP); 250 mixin JavaInterfaceMembers!(null); 251 } 252 253 /// I may not keep this. But for now if you need a dummy class in D 254 /// to represent some object that implements this interface in Java, 255 /// you can use this. The dummy class assumes all interface methods are @Imported. 256 static T dummyClass(T)(jobject obj) { 257 return new class T { 258 jobject getJavaHandle() { return obj; } 259 }; 260 } 261 262 263 /++ 264 Can be used as a UDA for methods or classes where the D name 265 and the Java name don't match (for example, if it happens to be 266 a D keyword). 267 268 --- 269 @JavaName("version") 270 @Import int version_(); 271 --- 272 +/ 273 struct JavaName { 274 string name; 275 } 276 277 private string getJavaName(alias a)() { 278 string name = __traits(identifier, a); 279 static foreach(attr; __traits(getAttributes, a)) 280 static if(is(typeof(attr) == JavaName)) 281 name = attr.name; 282 return name; 283 } 284 285 /+ 286 to benchmark build stats 287 cd ~/Android/d_android/java_bindings/android/java 288 /usr/bin/time -f "%E %M" dmd -o- -c `find . | grep -E '\.d$'` ~/arsd/jni.d -I../.. 289 +/ 290 291 /+ Java class file definitions { +/ 292 // see: https://docs.oracle.com/javase/specs/jvms/se13/html/jvms-4.html 293 294 version(WithClassLoadSupport) { 295 import arsd.declarativeloader; 296 297 /// translator. 298 void jarToD()(string jarPath, string dPackagePrefix, string outputDirectory, JavaTranslationConfig jtc, bool delegate(string className) classFilter = null) { 299 import std.zip; 300 import std.file; 301 import std.algorithm; 302 303 auto zip = new ZipArchive(read(jarPath)); 304 305 ClassFile[string] allClasses; 306 307 foreach(name, am; zip.directory) { 308 if(name.endsWith(".class")) { 309 zip.expand(am); 310 311 ClassFile cf; 312 313 auto classBytes = cast(ubyte[]) am.expandedData; 314 auto originalClassBytes = classBytes; 315 316 debug try { 317 cf.loadFrom!ClassFile(classBytes); 318 } catch(Exception e) { 319 std.file.write("spam.bin", originalClassBytes); 320 throw e; 321 } else 322 cf.loadFrom!ClassFile(classBytes); 323 324 string className = cf.className.idup; 325 326 if(classFilter is null || classFilter(className)) 327 allClasses[className] = cf; 328 329 //rawClassBytesToD(cast(ubyte[]) am.expandedData, dPackagePrefix, outputDirectory, jtc); 330 //am.expandedData = null; // let the GC take it if it wants 331 } 332 } 333 334 foreach(name, cf; allClasses) 335 rawClassStructToD(cf, dPackagePrefix, outputDirectory, jtc, allClasses); 336 } 337 338 private inout(char)[] fixupKeywordsInJavaPackageName(inout(char)[] s) { 339 import std.string; 340 s ~= "."; // lol i suck 341 s = s.replace(".function.", ".function_."); 342 s = s.replace(".ref.", ".ref_."); 343 return s[0 .. $-1]; // god i am such a bad programmer 344 } 345 346 private inout(char)[] fixupJavaClassName(inout(char)[] s) { 347 if(s == "Throwable" || s == "Object" || s == "Exception" || s == "Error" || s == "TypeInfo") 348 s = cast(typeof(s)) "Java" ~ s; 349 return s; 350 } 351 352 /// For the translator 353 struct JavaTranslationConfig { 354 /// List the Java methods, imported to D. 355 bool doImports; 356 /// List the native methods, assuming they should be exported from D 357 bool doExports; 358 /// Put implementations inline. If false, this separates interface from impl for quicker builds with dmd -i. 359 bool inlineImplementations; 360 /// Treat native functions as imports, otherwise fills in as exports. Make sure doImports == true. 361 bool nativesAreImports = true; 362 } 363 364 /// translator 365 void rawClassBytesToD()(ubyte[] bytes, string dPackagePrefix, string outputDirectory, JavaTranslationConfig jtc) { 366 ClassFile f; 367 f.loadFrom(bytes); 368 rawClassStructToD(f, dPackagePrefix, outputDirectory, jtc, null); 369 } 370 371 /// translator. 372 void rawClassStructToD()(ref ClassFile cf, string dPackagePrefix, string outputDirectory, JavaTranslationConfig jtc, ClassFile[string] allClasses) { 373 import std.file; 374 import std.path; 375 import std.algorithm; 376 import std.array; 377 import std.string; 378 379 string importPrefix = "import"; 380 381 const(char)[] javaPackage; 382 const(char)[] lastClassName; 383 384 const(char)[] originalJavaPackage; 385 const(char)[] originalClassName; 386 387 const(char)[] cn = cf.className; 388 auto idx = cn.lastIndexOf("/"); 389 if(idx != -1) { 390 javaPackage = cn[0 .. idx].replace("$", "_").replace("/", ".").fixupKeywordsInJavaPackageName; 391 lastClassName = cn[idx + 1 .. $]; 392 originalJavaPackage = cn[0 .. idx].replace("/", "."); 393 originalClassName = lastClassName; 394 } else { 395 lastClassName = cn; 396 originalJavaPackage = ""; 397 originalClassName = lastClassName; 398 } 399 400 lastClassName = lastClassName.replace("$", "_"); // NOTE rughs strings in this file 401 lastClassName = fixupJavaClassName(lastClassName); 402 403 auto filename = (outputDirectory.length ? (outputDirectory ~ "/") : "") 404 ~ (dPackagePrefix.length ? (dPackagePrefix.replace(".", "/") ~ "/") : "") 405 ~ javaPackage.replace(".", "/"); 406 mkdirRecurse(filename); 407 if(filename.length) 408 filename ~= "/"; 409 filename ~= lastClassName ~ ".d"; 410 411 if(filename.indexOf("-") != -1) 412 return; 413 414 415 string dco; 416 417 auto thisModule = cast(string)((dPackagePrefix.length ? (dPackagePrefix ~ ".") : "") ~ javaPackage); 418 if(thisModule.length && thisModule[$-1] != '.') 419 thisModule ~= "."; 420 thisModule ~= lastClassName; 421 422 bool isInterface = (cf.access_flags & 0x0200) ? true : false; 423 bool isAbstract = (cf.access_flags & ClassFile.ACC_ABSTRACT) ? true : false; 424 425 if(jtc.inlineImplementations) { 426 dco = "module " ~ thisModule ~ ";\n\n"; 427 } else { 428 dco ~= "module " ~ thisModule ~ "_d_interface;\n"; 429 } 430 431 dco ~= "import arsd.jni : IJavaObjectImplementation, JavaPackageId, JavaName, IJavaObject, ImportExportImpl, JavaInterfaceMembers;\n"; 432 dco ~= "static import arsd.jni;\n\n"; 433 434 string[string] javaPackages; 435 string[string] javaPackagesReturn; 436 string[string] javaPackagesArguments; 437 438 string dc; 439 if(lastClassName != originalClassName) 440 dc ~= "@JavaName(\""~originalClassName~"\")\n"; 441 442 bool outputMixinTemplate = false; 443 444 string mainThing; 445 //string helperThing; 446 447 // so overriding Java classes from D is iffy and with separate implementation 448 // non final leads to linker errors anyway... 449 //mainThing ~= (isInterface ? "interface " : (jtc.inlineImplementations ? "class " : isAbstract ? "abstract class " : "final class ")) ~ lastClassName ~ " : "; 450 451 mainThing ~= "final class " ~ lastClassName ~ " : IJavaObject {\n"; 452 mainThing ~= "\tstatic immutable string[] _d_canCastTo = [\n"; 453 454 // not putting super class on inline implementations since that forces vtable... 455 if(jtc.inlineImplementations) { 456 auto scn = cf.superclassName; 457 458 if(scn.length) { 459 mainThing ~= "\t\t\"" ~ scn ~ "\",\n"; 460 } 461 462 /+ 463 //if(!scn.startsWith("java/")) { 464 // superclasses need the implementation too so putting it in the return list lol 465 if(scn.length && scn != "java/lang/Object") { // && scn in allClasses) { 466 mainThing ~= javaObjectToDTypeString(scn, javaPackages, javaPackagesReturn, importPrefix); 467 mainThing ~= ", "; 468 } 469 //} 470 +/ 471 } 472 473 foreach(name; cf.interfacesNames) { 474 //if(name.startsWith("java/")) 475 //continue; // these probably aren't important to D and can really complicate version management 476 //if(name !in allClasses) 477 //continue; 478 //mainThing ~= javaObjectToDTypeString(name, javaPackages, javaPackagesReturn, importPrefix); 479 //mainThing ~= ", "; 480 481 mainThing ~= "\t\t\"" ~ name ~ "\",\n"; 482 } 483 484 mainThing ~= "\t];\n"; 485 486 //helperThing ~= "interface " ~ lastClassName ~ "_d_methods : "; 487 488 489 string[string] mentioned; 490 491 string[string] processed; 492 493 void addMethods(ClassFile* current, bool isTopLevel) { 494 if(current is null) return; 495 if(current.className in processed) return; 496 foreach(method; current.methodsListing) { 497 bool native = (method.flags & 0x0100) ? true : false; 498 if(jtc.nativesAreImports) { 499 native = false; // kinda hacky but meh 500 if(!jtc.doImports) 501 continue; 502 } else { 503 if(native && !jtc.doExports) 504 continue; 505 if(!native && !jtc.doImports) 506 continue; 507 } 508 auto port = native ? "@Export" : "@Import"; 509 if(method.flags & 1) { // public 510 if(!isTopLevel && method.name == "<init>") 511 continue; 512 513 bool maybeOverride = false;// !isInterface; 514 if(method.flags & 0x0008) { 515 port ~= " static"; 516 } 517 if(method.flags & method_info.ACC_ABSTRACT) { 518 //if(!isInterface) 519 //port ~= " abstract"; 520 } else { 521 // this represents a default implementation in a Java interface 522 // D cannot express this... so I need to add it to the mixin template 523 // associated with this interface as well. 524 //if(isInterface && (!(method.flags & 0x0008))) { 525 //addToMixinTemplate = true; 526 //} 527 } 528 529 //if(maybeOverride && method.isOverride(allClasses)) 530 //port ~= " override"; 531 532 auto name = method.name; 533 534 // FIXME: maybe check name for other D keywords but since so many overlap with java I think we will be ok most the time for now 535 if(name == "debug" || name == "delete" || name == "with" || name == "version" || name == "cast" || name == "union" || name == "align" || name == "alias" || name == "in" || name == "out" || name == "toString" || name == "init" || name == "lazy" || name == "immutable" || name == "is" || name == "function" || name == "delegate" || name == "template") { 536 // toString is special btw in order to avoid a dmd bug 537 port ~= " @JavaName(\""~name~"\")"; 538 name ~= "_"; 539 } 540 541 // NOTE rughs strings in this file 542 name = name.replace("$", "_"); 543 544 bool ctor = name == "<init>"; 545 546 auto sig = method.signature; 547 548 auto lidx = sig.lastIndexOf(")"); 549 assert(lidx != -1); 550 auto retJava = sig[lidx + 1 .. $]; 551 auto argsJava = sig[1 .. lidx]; 552 553 string ret = ctor ? "" : javaSignatureToDTypeString(retJava, javaPackages, javaPackagesReturn, importPrefix); 554 string args = javaSignatureToDTypeString(argsJava, javaPackages, javaPackagesArguments, importPrefix); 555 auto oargs = args; 556 557 if(!jtc.inlineImplementations) { 558 if(ctor && args.length == 0) 559 args = "arsd.jni.Default"; 560 } 561 562 string men = cast(immutable) (name ~ "(" ~ args ~ ")"); 563 if(men in mentioned) 564 continue; // avoid duplicate things. idk why this is there though 565 mentioned[men] = men; 566 567 string proto = cast(string) ("\t"~port~" " ~ ret ~ (ret.length ? " " : "") ~ (ctor ? "this" : name) ~ "("~args~")"~(native ? " { assert(0); }" : ";")~"\n"); 568 mainThing ~= proto; 569 570 if(oargs.length == 0 && name == "toString_" && !(method.flags & 0x0008)) 571 mainThing ~= "\toverride string toString() { return arsd.jni.javaObjectToString(this); }\n"; 572 } 573 } 574 575 processed[current.className.idup] = "done"; 576 if(current.superclassName.length) { 577 auto c = current.superclassName in allClasses; 578 addMethods(c, false); 579 } 580 foreach(iface; current.interfacesNames) { 581 auto c = iface in allClasses; 582 addMethods(c, false); 583 } 584 } 585 586 addMethods(&cf, true); 587 588 mainThing ~= "\tmixin IJavaObjectImplementation!(false);\n"; 589 mainThing ~= "\tpublic static immutable string _javaParameterString = \"L" ~ cn ~ ";\";\n"; 590 591 mainThing ~= "}\n\n"; 592 dc ~= mainThing; 593 dc ~= "\n\n"; 594 595 foreach(pkg, prefix; javaPackages) { 596 auto m = (dPackagePrefix.length ? (dPackagePrefix ~ ".") : "") ~ pkg; 597 // keeping thisModule because of the prefix nonsense 598 //if(m == thisModule) 599 //continue; 600 if(jtc.inlineImplementations) 601 dco ~= "import " ~ prefix ~ " = " ~ m ~ ";\n"; 602 else 603 dco ~= "import " ~ prefix ~ " = " ~ m ~ "_d_interface;\n"; 604 } 605 if(javaPackages.keys.length) 606 dco ~= "\n"; 607 dco ~= dc; 608 609 if(jtc.inlineImplementations) { 610 dco ~= "\nmixin ImportExportImpl!"~lastClassName~";\n"; 611 std.file.write(filename, dco); 612 } else { 613 string impl; 614 impl ~= "module " ~ thisModule ~ ";\n"; 615 impl ~= "public import " ~ thisModule ~ "_d_interface;\n\n"; 616 617 impl ~= "import arsd.jni : ImportExportImpl;\n"; 618 impl ~= "mixin ImportExportImpl!"~lastClassName~";\n"; 619 620 impl ~= "\n"; 621 foreach(pkg, prefix; javaPackagesReturn) { 622 // I also need to import implementations of return values so they just work 623 auto m = (dPackagePrefix.length ? (dPackagePrefix ~ ".") : "") ~ pkg; 624 impl ~= "import " ~ prefix ~ " = " ~ m ~ ";\n"; 625 } 626 627 std.file.write(filename, impl); 628 std.file.write(filename[0 .. $-2] ~ "_d_interface.d", dco); 629 } 630 } 631 632 string javaObjectToDTypeString(const(char)[] input, ref string[string] javaPackages, ref string[string] detailedPackages, string importPrefix) { 633 634 string ret; 635 636 if(input == "java/lang/String") { 637 ret = "string"; // or could be wstring... 638 } else if(input == "java/lang/Object") { 639 ret = "IJavaObject"; 640 } else { 641 // NOTE rughs strings in this file 642 string type = input.replace("$", "_").idup; 643 644 string jp, cn, dm; 645 646 auto idx = type.lastIndexOf("/"); 647 if(idx != -1) { 648 jp = type[0 .. idx].replace("/", ".").fixupKeywordsInJavaPackageName; 649 cn = type[idx + 1 .. $].fixupJavaClassName; 650 dm = jp ~ "." ~ cn; 651 } else { 652 cn = type; 653 dm = jp; 654 } 655 656 string prefix; 657 if(auto n = dm in javaPackages) { 658 prefix = *n; 659 } else { 660 import std.conv; 661 // FIXME: this scheme sucks, would prefer something deterministic 662 prefix = importPrefix ~ to!string(javaPackages.keys.length); 663 //prefix = dm.replace(".", "0"); 664 665 javaPackages[dm] = prefix; 666 detailedPackages[dm] = prefix; 667 } 668 669 ret = prefix ~ (prefix.length ? ".":"") ~ cn; 670 } 671 672 return ret; 673 } 674 675 string javaSignatureToDTypeString(ref const(char)[] js, ref string[string] javaPackages, ref string[string] detailedPackages, string importPrefix) { 676 string all; 677 678 while(js.length) { 679 string type; 680 switch(js[0]) { 681 case '[': 682 js = js[1 .. $]; 683 type = javaSignatureToDTypeString(js, javaPackages, detailedPackages, importPrefix); 684 type ~= "[]"; 685 break; 686 case 'L': 687 import std.string; 688 auto idx = js.indexOf(";"); 689 type = js[1 .. idx].idup; 690 js = js[idx + 1 .. $]; 691 692 type = javaObjectToDTypeString(type, javaPackages, detailedPackages, importPrefix); 693 break; 694 case 'V': js = js[1 .. $]; type = "void"; break; 695 case 'Z': js = js[1 .. $]; type = "bool"; break; 696 case 'B': js = js[1 .. $]; type = "byte"; break; 697 case 'C': js = js[1 .. $]; type = "wchar"; break; 698 case 'S': js = js[1 .. $]; type = "short"; break; 699 case 'J': js = js[1 .. $]; type = "long"; break; 700 case 'F': js = js[1 .. $]; type = "float"; break; 701 case 'D': js = js[1 .. $]; type = "double"; break; 702 case 'I': js = js[1 .. $]; type = "int"; break; 703 default: assert(0, js); 704 } 705 706 if(all.length) all ~= ", "; 707 all ~= type; 708 } 709 710 return all; 711 } 712 713 struct cp_info { 714 715 enum CONSTANT_Class = 7; // sizeof = 2 716 struct CONSTANT_Class_info { 717 @BigEndian: 718 ushort name_index; 719 } 720 enum CONSTANT_Fieldref = 9; // sizeof = 4 721 struct CONSTANT_Fieldref_info { 722 @BigEndian: 723 ushort class_index; 724 ushort name_and_type_index; 725 } 726 enum CONSTANT_Methodref = 10; // sizeof = 4 727 struct CONSTANT_Methodref_info { 728 @BigEndian: 729 ushort class_index; 730 ushort name_and_type_index; 731 } 732 enum CONSTANT_InterfaceMethodref = 11; // sizeof = 4 733 struct CONSTANT_InterfaceMethodref_info { 734 @BigEndian: 735 ushort class_index; 736 ushort name_and_type_index; 737 } 738 enum CONSTANT_String = 8; // sizeof = 2 739 struct CONSTANT_String_info { 740 @BigEndian: 741 ushort string_index; 742 } 743 enum CONSTANT_Integer = 3; // sizeof = 4 744 struct CONSTANT_Integer_info { 745 @BigEndian: 746 int bytes; 747 } 748 enum CONSTANT_Float = 4; // sizeof = 4 749 struct CONSTANT_Float_info { 750 @BigEndian: 751 float bytes; 752 } 753 enum CONSTANT_Long = 5; // sizeof = 8, but eats two slots 754 struct CONSTANT_Long_info { 755 @BigEndian: 756 long bytes; 757 } 758 enum CONSTANT_Double = 6; // sizeof = 8, but eats two slots 759 struct CONSTANT_Double_info { 760 @BigEndian: 761 double bytes; 762 } 763 enum CONSTANT_NameAndType = 12; // sizeof = 4 764 struct CONSTANT_NameAndType_info { 765 @BigEndian: 766 ushort name_index; 767 ushort descriptor_index; 768 } 769 enum CONSTANT_Utf8 = 1; // sizeof = 2 + length 770 struct CONSTANT_Utf8_info { 771 @BigEndian: 772 ushort length; 773 @NumElements!length char[] bytes; // actually modified UTF-8 but meh 774 } 775 enum CONSTANT_MethodHandle = 15; // sizeof = 3 776 struct CONSTANT_MethodHandle_info { 777 @BigEndian: 778 ubyte reference_kind; 779 ushort reference_index; 780 } 781 enum CONSTANT_MethodType = 16; // sizeof = 2; descriptor index 782 struct CONSTANT_MethodType_info { 783 @BigEndian: 784 ushort descriptor_index; 785 } 786 enum CONSTANT_InvokeDynamic = 18; // sizeof = 4 787 struct CONSTANT_InvokeDynamic_info { 788 @BigEndian: 789 ushort bootstrap_method_attr_index; 790 ushort name_and_type_index; 791 } 792 enum CONSTANT_Module = 19; 793 struct CONSTANT_Module_info { 794 @BigEndian: 795 ushort name_index; 796 } 797 enum CONSTANT_Package = 20; 798 struct CONSTANT_Package_info { 799 @BigEndian: 800 ushort name_index; 801 } 802 803 804 805 ubyte tag; 806 @Tagged!(tag) 807 union Info { 808 @Tag(CONSTANT_Class) CONSTANT_Class_info class_info; 809 @Tag(CONSTANT_Fieldref) CONSTANT_Fieldref_info fieldref_info; 810 @Tag(CONSTANT_Methodref) CONSTANT_Methodref_info methodref_info; 811 @Tag(CONSTANT_InterfaceMethodref) CONSTANT_InterfaceMethodref_info interfaceMethodref_info; 812 @Tag(CONSTANT_String) CONSTANT_String_info string_info; 813 @Tag(CONSTANT_Integer) CONSTANT_Integer_info integer_info; 814 @Tag(CONSTANT_Float) CONSTANT_Float_info float_info; 815 @Tag(CONSTANT_Long) CONSTANT_Long_info long_info; 816 @Tag(CONSTANT_Double) CONSTANT_Double_info double_info; 817 @Tag(CONSTANT_NameAndType) CONSTANT_NameAndType_info nameAndType_info; 818 @Tag(CONSTANT_Utf8) CONSTANT_Utf8_info utf8_info; 819 @Tag(CONSTANT_MethodHandle) CONSTANT_MethodHandle_info methodHandle_info; 820 @Tag(CONSTANT_MethodType) CONSTANT_MethodType_info methodType_info; 821 @Tag(CONSTANT_InvokeDynamic) CONSTANT_InvokeDynamic_info invokeDynamic_info; 822 @Tag(CONSTANT_Module) CONSTANT_Module_info module_info; 823 @Tag(CONSTANT_Package) CONSTANT_Package_info package_info; 824 } 825 Info info; 826 827 bool takesTwoSlots() { 828 return (tag == CONSTANT_Long || tag == CONSTANT_Double); 829 } 830 831 string toString() { 832 if(tag == CONSTANT_Utf8) 833 return cast(string) info.utf8_info.bytes; 834 import std.format; 835 return format("cp_info(%s)", tag); 836 } 837 } 838 839 struct field_info { 840 @BigEndian: 841 842 enum ACC_PUBLIC = 0x0001; 843 enum ACC_PRIVATE = 0x0002; 844 enum ACC_PROTECTED = 0x0004; 845 enum ACC_STATIC = 0x0008; 846 enum ACC_FINAL = 0x0010; 847 enum ACC_VOLATILE = 0x0040; 848 enum ACC_TRANSIENT = 0x0080; 849 enum ACC_SYNTHETIC = 0x1000; 850 enum ACC_ENUM = 0x4000; 851 852 ushort access_flags; 853 ushort name_index; 854 ushort descriptor_index; 855 ushort attributes_count; 856 @NumElements!attributes_count attribute_info[] attributes; 857 } 858 859 struct method_info { 860 @BigEndian: 861 ushort access_flags; 862 ushort name_index; 863 ushort descriptor_index; 864 ushort attributes_count; 865 @NumElements!attributes_count attribute_info[] attributes; 866 867 enum ACC_PUBLIC = 0x0001; 868 enum ACC_PRIVATE = 0x0002; 869 enum ACC_PROTECTED = 0x0004; 870 enum ACC_STATIC = 0x0008; 871 enum ACC_FINAL = 0x0010; 872 enum ACC_SYNCHRONIZED = 0x0020; 873 enum ACC_BRIDGE = 0x0040; 874 enum ACC_VARARGS = 0x0080; 875 enum ACC_NATIVE = 0x0100; 876 enum ACC_ABSTRACT = 0x0400; 877 enum ACC_STRICT = 0x0800; 878 enum ACC_SYNTHETIC = 0x1000; 879 } 880 881 struct attribute_info { 882 @BigEndian: 883 ushort attribute_name_index; 884 uint attribute_length; 885 @NumBytes!attribute_length ubyte[] info; 886 } 887 888 struct ClassFile { 889 @BigEndian: 890 891 892 enum ACC_PUBLIC = 0x0001; 893 enum ACC_FINAL = 0x0010; 894 enum ACC_SUPER = 0x0020; 895 enum ACC_INTERFACE = 0x0200; 896 enum ACC_ABSTRACT = 0x0400; 897 enum ACC_SYNTHETIC = 0x1000; 898 enum ACC_ANNOTATION = 0x2000; 899 enum ACC_ENUM = 0x4000; 900 901 const(char)[] className() { 902 return this.constant(this.constant(this.this_class).info.class_info.name_index).info.utf8_info.bytes; 903 } 904 905 const(char)[] superclassName() { 906 if(this.super_class) 907 return this.constant(this.constant(this.super_class).info.class_info.name_index).info.utf8_info.bytes; 908 return null; 909 } 910 911 const(char)[][] interfacesNames() { 912 typeof(return) ret; 913 foreach(iface; interfaces) { 914 ret ~= this.constant(this.constant(iface).info.class_info.name_index).info.utf8_info.bytes; 915 } 916 return ret; 917 } 918 919 Method[] methodsListing() { 920 Method[] ms; 921 foreach(met; this.methods) { 922 Method m; 923 m.name = this.constant(met.name_index).info.utf8_info.bytes; 924 m.signature = this.constant(met.descriptor_index).info.utf8_info.bytes; 925 m.flags = met.access_flags; 926 m.cf = &this; 927 ms ~= m; 928 } 929 return ms; 930 } 931 932 bool hasConcreteMethod(const(char)[] name, const(char)[] signature, ClassFile[string] allClasses) { 933 // I don't really care cuz I don't use the same root in D 934 if(this.className == "java/lang/Object") 935 return false; 936 937 foreach(m; this.methodsListing) { 938 if(m.name == name)// && m.signature == signature) 939 return true; 940 //return (m.flags & method_info.ACC_ABSTRACT) ? false : true; // abstract impls do not count as methods as far as overrides are concerend... 941 } 942 943 if(auto s = this.superclassName in allClasses) 944 return s.hasConcreteMethod(name, signature, allClasses); 945 return false; 946 } 947 948 static struct Method { 949 const(char)[] name; 950 const(char)[] signature; 951 ushort flags; 952 ClassFile* cf; 953 bool isOverride(ClassFile[string] allClasses) { 954 if(name == "<init>") 955 return false; 956 if(auto s = cf.superclassName in allClasses) 957 return s.hasConcreteMethod(name, signature, allClasses); 958 return false; 959 } 960 } 961 962 963 @MustBe(0xcafebabe) uint magic; 964 ushort minor_version; 965 ushort major_version; 966 ushort constant_pool_count_; 967 // the zeroth item of the constant pool is null, but not actually in the file. 968 ushort constant_pool_count() { return cast(ushort)(constant_pool_count_ - 1); } 969 auto constant(ushort number) { 970 if(number == 0) throw new Exception("invalid"); 971 return constant_pool[number - 1]; 972 } 973 @NumElements!constant_pool_count cp_info[] constant_pool; 974 ushort access_flags; 975 ushort this_class; 976 ushort super_class; 977 ushort interfaces_count; 978 @NumElements!interfaces_count ushort[] interfaces; 979 ushort fields_count; 980 @NumElements!fields_count field_info[] fields; 981 ushort methods_count; 982 @NumElements!methods_count method_info[] methods; 983 ushort attributes_count; 984 @NumElements!attributes_count attribute_info[] attributes; 985 } 986 987 } 988 989 /+ } end java class file definitions +/ 990 991 // semi-FIXME: java.lang.CharSequence is the interface for String. We should support that just as well. 992 // possibly other boxed types too, like Integer. 993 // FIXME: in general, handle substituting subclasses for interfaces nicely 994 995 // FIXME: solve the globalref/pin issue with the type system 996 997 // 998 999 // FIXME: what about the parent class of the java object? Best we can probably do is an interface but perhaps it could be auto-generated by the JavaClass magic. It could take the list and just copy the @Import items. 1000 1001 // FIXME: interfaces? I think a Java interface should just generally be turned into a D interface, but also including the IJavaObject. Basically just write D. No @Import or @Export on this level. 1002 // Just needs a package name somewhere.... 1003 // 1004 // Then the D compiler forces you to declare an implementation of it, and that can be @Import. 1005 1006 /+ 1007 FIXME lol if i wanted to try defining a new class in D..... you don't even need a trampoline method. Java and native methods can override each other!!! 1008 1009 1010 Perhaps could be like final class AllNew : JavaClass("package", AllNew, true) { 1011 @Virtual void foo() {} // defines it here, but Java can override 1012 @Override void bar() {} // overrides existing thing with new impl 1013 } 1014 and then @Import and @Export continues to work the same way. 1015 +/ 1016 1017 // FIXME: speaking of hacking bytecode we could prolly read signatures out of a .class file too. 1018 // and generate D classes :P 1019 1020 // see: https://developer.android.com/training/articles/perf-jni.html 1021 1022 // I doubt I can do anything with Java generics through this except doing it as an object array but maybe a FIXME? 1023 1024 //pragma(crt_constructor) // fyi 1025 //pragma(crt_destructor) 1026 1027 extern(System) 1028 export jint JNI_OnLoad(JavaVM* vm, void* reserved) { 1029 try { 1030 import core.runtime; 1031 // note this is OK if it is already initialized 1032 // since it refcounts 1033 Runtime.initialize(); 1034 } catch(Throwable t) { 1035 return JNI_ERR; 1036 } 1037 1038 activeJvm = vm; 1039 1040 JNIEnv* env; 1041 if ((*vm).GetEnv(vm, cast(void**) &env, JNI_VERSION_DESIRED) != JNI_OK) { 1042 return JNI_ERR; 1043 } 1044 1045 try { 1046 foreach(init; classInitializers_) 1047 if(init(env) != 0) 1048 {}//return JNI_ERR; 1049 foreach(init; newClassInitializers_) 1050 if(init(env) != 0) 1051 return JNI_ERR; 1052 } catch(Throwable t) { 1053 import core.stdc.stdio; 1054 fprintf(stderr, "%s", (t.toString ~ "\n\0").ptr); 1055 return JNI_ERR; 1056 } 1057 1058 return JNI_VERSION_DESIRED; 1059 } 1060 extern(System) 1061 export void JNI_OnUnload(JavaVM* vm, void* reserved) { 1062 activeJvm = null; 1063 import core.runtime; 1064 try { 1065 // note the refcount is upped in JNI_OnLoad 1066 Runtime.terminate(); 1067 } catch(Throwable t) { 1068 import core.stdc.stdlib; 1069 abort(); 1070 } 1071 } 1072 1073 __gshared JavaVM* activeJvm; 1074 1075 // need this for Import functions 1076 JNIEnv* activeEnv; 1077 struct ActivateJniEnv { 1078 // this will put it on a call stack so it will be 1079 // sane through re-entrant situations 1080 JNIEnv* old; 1081 this(JNIEnv* e) { 1082 old = activeEnv; 1083 activeEnv = e; 1084 } 1085 1086 ~this() { 1087 activeEnv = old; 1088 } 1089 } 1090 1091 // FIXME figure out threads... 1092 1093 /++ 1094 Creates a JVM for use when `main` is in D. Keep the returned 1095 struct around until you are done with it. While this struct 1096 is live, you can use imported Java classes and methods almost 1097 as if they were written in D. 1098 1099 If `main` is in Java, this is not necessary and should not be 1100 used. 1101 1102 1103 This function will try to load the jvm with dynamic runtime 1104 linking. For this to succeed: 1105 1106 On Windows, make sure the path to `jvm.dll` is in your `PATH` 1107 environment variable, or installed system wide. For example: 1108 1109 $(CONSOLE 1110 set PATH=%PATH%;c:\users\me\program\jdk-13.0.1\bin\server 1111 ) 1112 1113 On Linux (and I think Mac), set `LD_LIBRARY_PATH` environment 1114 variable to include the path to `libjvm.so`. 1115 1116 $(CONSOLE 1117 export LD_LIBRARY_PATH=/home/me/jdk-13.0.1/bin/server 1118 --- or maybe --- 1119 LD_LIBRARY_PATH=/opt/android/android-studio/jre/jre/lib/amd64/server ./myjvm 1120 ) 1121 1122 Failure to do this will throw an exception along the lines of 1123 "no jvm dll" in the message. That error can also be thrown if 1124 you have a 32 bit program but try to load a 64 bit JVM, or vice 1125 versa. 1126 +/ 1127 auto createJvm()() { 1128 version(Windows) 1129 import core.sys.windows.windows; 1130 else 1131 import core.sys.posix.dlfcn; 1132 1133 static struct JVM { 1134 ActivateJniEnv e; 1135 JavaVM* pvm; 1136 void* jvmdll; 1137 1138 @disable this(this); 1139 1140 ~this() { 1141 if(pvm) 1142 (*pvm).DestroyJavaVM(pvm); 1143 activeJvm = null; 1144 1145 version(Windows) { 1146 if(jvmdll) FreeLibrary(jvmdll); 1147 } else { 1148 if(jvmdll) dlclose(jvmdll); 1149 } 1150 } 1151 } 1152 1153 JavaVM* pvm; 1154 JNIEnv* env; 1155 1156 JavaVMInitArgs vm_args; 1157 JavaVMOption[0] options; 1158 1159 //options[0].optionString = "-Djava.compiler=NONE"; /* disable JIT */ 1160 //options[1].optionString = "-verbose:jni"; /* print JNI-related messages */ 1161 //options[1].optionString = `-Djava.class.path=c:\Users\me\program\jni\`; /* user classes */ 1162 //options[2].optionString = `-Djava.library.path=c:\Users\me\program\jdk-13.0.1\lib\`; /* set native library path */ 1163 1164 vm_args.version_ = JNI_VERSION_DESIRED; 1165 vm_args.options = options.ptr; 1166 vm_args.nOptions = cast(int) options.length; 1167 vm_args.ignoreUnrecognized = true; 1168 1169 //import std.process; 1170 //environment["PATH"] = environment["PATH"] ~ `;c:\users\me\program\jdk-13.0.1\bin\server`; 1171 1172 version(Windows) 1173 auto jvmdll = LoadLibraryW("jvm.dll"w.ptr); 1174 else 1175 auto jvmdll = dlopen("libjvm.so", RTLD_LAZY); 1176 1177 if(jvmdll is null) 1178 throw new Exception("no jvm dll"); 1179 1180 version(Windows) 1181 auto fn = cast(typeof(&JNI_CreateJavaVM)) GetProcAddress(jvmdll, "JNI_CreateJavaVM"); 1182 else 1183 auto fn = cast(typeof(&JNI_CreateJavaVM)) dlsym(jvmdll, "JNI_CreateJavaVM"); 1184 1185 if(fn is null) 1186 throw new Exception("no fun"); 1187 1188 auto res = fn(&pvm, cast(void**) &env, &vm_args);//, args); 1189 if(res != JNI_OK) 1190 throw new Exception("create jvm failed"); // FIXME: throw res); 1191 1192 activeJvm = pvm; 1193 1194 return JVM(ActivateJniEnv(env), pvm, jvmdll); 1195 } 1196 1197 version(Windows) 1198 private extern(Windows) bool SetDllDirectoryW(wstring); 1199 1200 1201 @JavaName("Throwable") 1202 final class JavaThrowable : JavaClass!("java.lang", JavaThrowable) { 1203 @Import string getMessage(); 1204 @Import StackTraceElement[] getStackTrace(); 1205 } 1206 1207 final class StackTraceElement : JavaClass!("java.lang", StackTraceElement) { 1208 @Import this(string declaringClass, string methodName, string fileName, int lineNumber); 1209 @Import string getClassName(); 1210 @Import string getFileName(); 1211 @Import int getLineNumber(); 1212 @Import string getMethodName(); 1213 @Import bool isNativeMethod(); 1214 } 1215 1216 private void exceptionCheck(JNIEnv* env) { 1217 if((*env).ExceptionCheck(env)) { 1218 (*env).ExceptionDescribe(env); // prints it to stderr, not that interesting 1219 jthrowable thrown = (*env).ExceptionOccurred(env); 1220 // do I need to free thrown? 1221 (*env).ExceptionClear(env); 1222 1223 // FIXME 1224 throw new Exception("Java threw"); 1225 } 1226 } 1227 1228 private enum ImportImplementationString = q{ 1229 static if(is(typeof(return) == void)) { 1230 (*env).CallSTATICVoidMethod(env, jobj, _jmethodID, DDataToJni(env, args).args); 1231 exceptionCheck(env); 1232 } else static if(is(typeof(return) == string) || is(typeof(return) == wstring)) { 1233 // I can't just use JavaParamsToD here btw because of lifetime worries. 1234 // maybe i should fix it down there though because there is a lot of duplication 1235 1236 auto jret = (*env).CallSTATICObjectMethod(env, jobj, _jmethodID, DDataToJni(env, args).args); 1237 exceptionCheck(env); 1238 1239 typeof(return) ret; 1240 1241 auto len = (*env).GetStringLength(env, jret); 1242 auto ptr = (*env).GetStringChars(env, jret, null); 1243 static if(is(typeof(return) == wstring)) { 1244 if(ptr !is null) { 1245 ret = ptr[0 .. len].idup; 1246 (*env).ReleaseStringChars(env, jret, ptr); 1247 } 1248 } else { 1249 import std.conv; 1250 if(ptr !is null) { 1251 ret = to!string(ptr[0 .. len]); 1252 (*env).ReleaseStringChars(env, jret, ptr); 1253 } 1254 } 1255 1256 return ret; 1257 } else static if(is(typeof(return) == int)) { 1258 auto ret = (*env).CallSTATICIntMethod(env, jobj, _jmethodID, DDataToJni(env, args).args); 1259 exceptionCheck(env); 1260 return ret; 1261 } else static if(is(typeof(return) == short)) { 1262 auto ret = (*env).CallSTATICShortMethod(env, jobj, _jmethodID, DDataToJni(env, args).args); 1263 exceptionCheck(env); 1264 return ret; 1265 } else static if(is(typeof(return) : IJavaObject)) { 1266 auto ret = (*env).CallSTATICObjectMethod(env, jobj, _jmethodID, DDataToJni(env, args).args); 1267 exceptionCheck(env); 1268 return fromExistingJavaObject!(typeof(return))(ret); 1269 } else static if(is(typeof(return) == long)) { 1270 auto ret = (*env).CallSTATICLongMethod(env, jobj, _jmethodID, DDataToJni(env, args).args); 1271 exceptionCheck(env); 1272 return ret; 1273 } else static if(is(typeof(return) == float)) { 1274 auto ret = (*env).CallSTATICFloatMethod(env, jobj, _jmethodID, DDataToJni(env, args).args); 1275 exceptionCheck(env); 1276 return ret; 1277 } else static if(is(typeof(return) == double)) { 1278 auto ret = (*env).CallSTATICDoubleMethod(env, jobj, _jmethodID, DDataToJni(env, args).args); 1279 exceptionCheck(env); 1280 return ret; 1281 } else static if(is(typeof(return) == bool)) { 1282 auto ret = (*env).CallSTATICBooleanMethod(env, jobj, _jmethodID, DDataToJni(env, args).args); 1283 exceptionCheck(env); 1284 return ret; 1285 } else static if(is(typeof(return) == byte)) { 1286 auto ret = (*env).CallSTATICByteMethod(env, jobj, _jmethodID, DDataToJni(env, args).args); 1287 exceptionCheck(env); 1288 return ret; 1289 } else static if(is(typeof(return) == wchar)) { 1290 auto ret = (*env).CallSTATICCharMethod(env, jobj, _jmethodID, DDataToJni(env, args).args); 1291 exceptionCheck(env); 1292 return ret; 1293 } else static if(is(typeof(return) == E[], E)) { 1294 // Java arrays are represented as objects 1295 auto jarr = (*env).CallSTATICObjectMethod(env, jobj, _jmethodID, DDataToJni(env, args).args); 1296 exceptionCheck(env); 1297 1298 auto len = (*env).GetArrayLength(env, jarr); 1299 static if(is(E == int)) { 1300 auto eles = (*env).GetIntArrayElements(env, jarr, null); 1301 auto res = eles[0 .. len]; 1302 (*env).ReleaseIntArrayElements(env, jarr, eles, 0); 1303 } else static if(is(E == bool)) { 1304 auto eles = (*env).GetBooleanArrayElements(env, jarr, null); 1305 auto res = eles[0 .. len]; 1306 (*env).ReleaseBooleanArrayElements(env, jarr, eles, 0); 1307 } else static if(is(E == long)) { 1308 auto eles = (*env).GetLongArrayElements(env, jarr, null); 1309 auto res = eles[0 .. len]; 1310 (*env).ReleaseLongArrayElements(env, jarr, eles, 0); 1311 } else static if(is(E == short)) { 1312 auto eles = (*env).GetShortArrayElements(env, jarr, null); 1313 auto res = eles[0 .. len]; 1314 (*env).ReleaseShortArrayElements(env, jarr, eles, 0); 1315 } else static if(is(E == wchar)) { 1316 auto eles = (*env).GetCharArrayElements(env, jarr, null); 1317 auto res = eles[0 .. len]; 1318 (*env).ReleaseCharArrayElements(env, jarr, eles, 0); 1319 } else static if(is(E == float)) { 1320 auto eles = (*env).GetFloatArrayElements(env, jarr, null); 1321 auto res = eles[0 .. len]; 1322 (*env).ReleaseFloatArrayElements(env, jarr, eles, 0); 1323 } else static if(is(E == double)) { 1324 auto eles = (*env).GetDoubleArrayElements(env, jarr, null); 1325 auto res = eles[0 .. len]; 1326 (*env).ReleaseDoubleArrayElements(env, jarr, eles, 0); 1327 } else static if(is(E == byte)) { 1328 auto eles = (*env).GetByteArrayElements(env, jarr, null); 1329 auto res = eles[0 .. len]; 1330 (*env).ReleaseByteArrayElements(env, jarr, eles, 0); 1331 } else static if(is(E == string)) { 1332 /* 1333 auto eles = (*env).GetByteArrayElements(env, jarr, null); 1334 auto res = eles[0 .. len]; 1335 (*env).ReleaseByteArrayElements(env, jarr, eles, 0); 1336 */ 1337 string[] res; // FIXME 1338 } else static if(is(E : IJavaObject)) { 1339 // FIXME: implement this 1340 typeof(return) res = null; 1341 } else static if(true) { 1342 E[] res; // FIXME FIXME 1343 } else static assert(0, E.stringof ~ " not supported array element type yet"); // FIXME handle object arrays too. which would also prolly include arrays of arrays. 1344 1345 return res; 1346 } else { 1347 static assert(0, "Unsupported return type for JNI: " ~ typeof(return).stringof); 1348 //return DDataToJni(env, __traits(getMember, dobj, __traits(identifier, method))(JavaParamsToD!(Parameters!method)(env, args).args)); 1349 } 1350 }; 1351 1352 import std.string; 1353 static immutable ImportImplementationString_static = ImportImplementationString.replace("STATIC", "Static"); 1354 static immutable ImportImplementationString_not = ImportImplementationString.replace("STATIC", ""); 1355 1356 private mixin template JavaImportImpl(T, alias method, size_t overloadIndex) { 1357 import std.traits; 1358 1359 private static jmethodID _jmethodID; 1360 1361 static if(__traits(identifier, method) == "__ctor") 1362 pragma(mangle, method.mangleof) 1363 private static T implementation(Parameters!method args, T this_) { 1364 auto env = activeEnv; 1365 if(env is null) 1366 throw new Exception("JNI not active in this thread"); 1367 1368 if(!_jmethodID) { 1369 jclass jc; 1370 if(!T.internalJavaClassHandle_) { 1371 jc = (*env).FindClass(env, (T._javaParameterString[1 .. $-1] ~ "\0").ptr); 1372 if(!jc) 1373 throw new Exception("Cannot find Java class " ~ T._javaParameterString[1 .. $-1]); 1374 T.internalJavaClassHandle_ = jc; 1375 } else { 1376 jc = T.internalJavaClassHandle_; 1377 } 1378 static if(args.length == 1 && is(typeof(args[0]) == arsd.jni.Default)) 1379 _jmethodID = (*env).GetMethodID(env, jc, 1380 "<init>", 1381 // java method string is (args)ret 1382 ("()V\0").ptr 1383 ); 1384 else 1385 _jmethodID = (*env).GetMethodID(env, jc, 1386 "<init>", 1387 // java method string is (args)ret 1388 ("(" ~ DTypesToJniString!(typeof(args)) ~ ")V\0").ptr 1389 ); 1390 1391 if(!_jmethodID) 1392 throw new Exception("Cannot find static Java method " ~ T.stringof ~ "." ~ __traits(identifier, method)); 1393 } 1394 1395 static if(args.length == 1 && is(typeof(args[0]) == arsd.jni.Default)) 1396 auto o = (*env).NewObject(env, T.internalJavaClassHandle_, _jmethodID); 1397 else 1398 auto o = (*env).NewObject(env, T.internalJavaClassHandle_, _jmethodID, DDataToJni(env, args).args); 1399 this_.internalJavaHandle_ = o; 1400 return this_; 1401 } 1402 else static if(__traits(isStaticFunction, method)) 1403 pragma(mangle, method.mangleof) 1404 private static ReturnType!method implementation(Parameters!method args) { 1405 auto env = activeEnv; 1406 if(env is null) 1407 throw new Exception("JNI not active in this thread"); 1408 1409 if(!_jmethodID) { 1410 jclass jc; 1411 if(!T.internalJavaClassHandle_) { 1412 jc = (*env).FindClass(env, (T._javaParameterString[1 .. $-1] ~ "\0").ptr); 1413 if(!jc) 1414 throw new Exception("Cannot find Java class " ~ T._javaParameterString[1 .. $-1]); 1415 T.internalJavaClassHandle_ = jc; 1416 } else { 1417 jc = T.internalJavaClassHandle_; 1418 } 1419 _jmethodID = (*env).GetStaticMethodID(env, jc, 1420 getJavaName!method.ptr, 1421 // java method string is (args)ret 1422 ("(" ~ DTypesToJniString!(typeof(args)) ~ ")" ~ DTypesToJniString!(typeof(return)) ~ "\0").ptr 1423 ); 1424 1425 if(!_jmethodID) 1426 throw new Exception("Cannot find static Java method " ~ T.stringof ~ "." ~ __traits(identifier, method)); 1427 } 1428 1429 auto jobj = T.internalJavaClassHandle_; 1430 1431 mixin(ImportImplementationString_static); 1432 } 1433 else 1434 pragma(mangle, method.mangleof) 1435 private static ReturnType!method implementation(Parameters!method args, T this_) { 1436 auto env = activeEnv; 1437 if(env is null) 1438 throw new Exception("JNI not active in this thread"); 1439 1440 auto jobj = this_.getJavaHandle(); 1441 if(!_jmethodID) { 1442 auto jc = (*env).GetObjectClass(env, jobj); 1443 // just a note: jc is an instance of java.lang.Class 1444 // and we could call getName on it to fetch a String to ID it 1445 _jmethodID = (*env).GetMethodID(env, jc, 1446 getJavaName!method.ptr, 1447 // java method string is (args)ret 1448 ("(" ~ DTypesToJniString!(typeof(args)) ~ ")" ~ DTypesToJniString!(typeof(return)) ~ "\0").ptr 1449 ); 1450 1451 if(!_jmethodID) 1452 throw new Exception("Cannot find Java method " ~ T.stringof ~ "." ~ __traits(identifier, method)); 1453 } 1454 1455 mixin(ImportImplementationString_not); 1456 } 1457 } 1458 1459 private template DTypesToJniString(Types...) { 1460 static if(Types.length == 0) 1461 enum string DTypesToJniString = ""; 1462 else static if(Types.length == 1) { 1463 alias T = Types[0]; 1464 1465 static if(is(T == void)) 1466 enum string DTypesToJniString = "V"; 1467 else static if(is(T == string)) { 1468 enum string DTypesToJniString = "Ljava/lang/String;"; 1469 } else static if(is(T == wstring)) 1470 enum string DTypesToJniString = "Ljava/lang/String;"; 1471 else static if(is(T == int)) 1472 enum string DTypesToJniString = "I"; 1473 else static if(is(T == bool)) 1474 enum string DTypesToJniString = "Z"; 1475 else static if(is(T == byte)) 1476 enum string DTypesToJniString = "B"; 1477 else static if(is(T == wchar)) 1478 enum string DTypesToJniString = "C"; 1479 else static if(is(T == short)) 1480 enum string DTypesToJniString = "S"; 1481 else static if(is(T == long)) 1482 enum string DTypesToJniString = "J"; 1483 else static if(is(T == float)) 1484 enum string DTypesToJniString = "F"; 1485 else static if(is(T == double)) 1486 enum string DTypesToJniString = "D"; 1487 else static if(is(T == size_t)) 1488 enum string DTypesToJniString = "I"; // possible FIXME... 1489 else static if(is(T == IJavaObject)) 1490 enum string DTypesToJniString = "LObject;"; // FIXME? 1491 else static if(is(T : IJavaObject)) // child of this but a concrete type 1492 enum string DTypesToJniString = T._javaParameterString; 1493 else static if(is(T == E[], E)) 1494 enum string DTypesToJniString = "[" ~ DTypesToJniString!E; 1495 else static assert(0, "Unsupported type for JNI call " ~ T.stringof); 1496 } else { 1497 private string helper() { 1498 string s; 1499 foreach(Type; Types) 1500 s ~= DTypesToJniString!Type; 1501 return s; 1502 } 1503 enum string DTypesToJniString = helper; 1504 } 1505 } 1506 1507 1508 private template DTypesToJni(Types...) { 1509 static if(Types.length == 0) 1510 alias DTypesToJni = Types; 1511 else static if(Types.length == 1) { 1512 alias T = Types[0]; 1513 1514 static if(is(T == void)) 1515 alias DTypesToJni = void; 1516 else static if(is(T == string)) 1517 alias DTypesToJni = jstring; 1518 else static if(is(T == wstring)) 1519 alias DTypesToJni = jstring; 1520 else static if(is(T == int)) 1521 alias DTypesToJni = jint; 1522 else static if(is(T == bool)) 1523 alias DTypesToJni = jboolean; 1524 else static if(is(T == byte)) 1525 alias DTypesToJni = jbyte; 1526 else static if(is(T == wchar)) 1527 alias DTypesToJni = jchar; 1528 else static if(is(T == short)) 1529 alias DTypesToJni = jshort; 1530 else static if(is(T == long)) 1531 alias DTypesToJni = jlong; 1532 else static if(is(T == float)) 1533 alias DTypesToJni = jfloat; 1534 else static if(is(T == double)) 1535 alias DTypesToJni = jdouble; 1536 else static if(is(T == size_t)) 1537 alias DTypesToJni = jsize; 1538 else static if(is(T : IJavaObject)) 1539 alias DTypesToJni = jobject; 1540 else static if(is(T == IJavaObject[])) 1541 alias DTypesToJni = jobjectArray; 1542 else static if(is(T == bool[])) 1543 alias DTypesToJni = jbooleanArray; 1544 else static if(is(T == byte[])) 1545 alias DTypesToJni = jbyteArray; 1546 else static if(is(T == wchar[])) 1547 alias DTypesToJni = jcharArray; 1548 else static if(is(T == short[])) 1549 alias DTypesToJni = jshortArray; 1550 else static if(is(T == int[])) 1551 alias DTypesToJni = jintArray; 1552 else static if(is(T == long[])) 1553 alias DTypesToJni = jlongArray; 1554 else static if(is(T == float[])) 1555 alias DTypesToJni = jfloatArray; 1556 else static if(is(T == double[])) 1557 alias DTypesToJni = jdoubleArray; 1558 else static if(is(T == string[])) // prolly FIXME 1559 alias DTypesToJni = jobjectArray; 1560 else static if(is(T == E[], E)) // FIXME!!!!!!! 1561 alias DTypesToJni = jobjectArray; 1562 else static assert(0, "Unsupported type for JNI: " ~ T.stringof); 1563 } else { 1564 import std.meta; 1565 // FIXME: write about this later if you forget the ! on the final DTypesToJni, dmd 1566 // says "error: recursive template expansion". woof. 1567 alias DTypesToJni = AliasSeq!(DTypesToJni!(Types[0]), DTypesToJni!(Types[1 .. $])); 1568 } 1569 } 1570 1571 auto DDataToJni(T...)(JNIEnv* env, T data) { 1572 import std.meta; 1573 struct Tmp { 1574 AliasSeq!(DTypesToJni!(T)) args; 1575 } 1576 1577 Tmp t; 1578 foreach(idx, ref arg; t.args) 1579 arg = DDatumToJni(env, data[idx]); 1580 return t; 1581 } 1582 1583 auto DDatumToJni(T)(JNIEnv* env, T data) { 1584 static if(is(T == void)) 1585 static assert(0); 1586 else static if(is(T == string)) { 1587 if(data is null) 1588 return null; 1589 wchar[1024] buffer; 1590 const(wchar)[] translated; 1591 if(data.length < 1024) { 1592 size_t len; 1593 foreach(wchar ch; data) 1594 buffer[len++] = ch; 1595 translated = buffer[0 .. len]; 1596 } else { 1597 import std.conv; 1598 translated = to!wstring(data); 1599 } 1600 // Java copies the buffer so it is perfectly fine to return here now 1601 return (*env).NewString(env, translated.ptr, cast(jsize) translated.length); 1602 } else static if(is(T == wstring)) 1603 return (*env).NewString(env, data.ptr, cast(jsize) data.length); 1604 else static if(is(T == int)) return data; 1605 else static if(is(T == bool)) return data; 1606 else static if(is(T == byte)) return data; 1607 else static if(is(T == wchar)) return data; 1608 else static if(is(T == short)) return data; 1609 else static if(is(T == long)) return data; 1610 else static if(is(T == float)) return data; 1611 else static if(is(T == double)) return data; 1612 1613 else static if(is(T == size_t)) return cast(int) data; 1614 else static if(is(T : IJavaObject)) return data is null ? null : data.getJavaHandle(); 1615 1616 1617 else static if(is(T == string[])) return null; // FIXME!!! 1618 1619 else static if(is(T == IJavaObject[])) return null; // FIXME!!! 1620 else static if(is(T == bool[])) return null; // FIXME!!! 1621 else static if(is(T == byte[])) return null; // FIXME!!! 1622 else static if(is(T == wchar[])) return null; // FIXME!!! 1623 else static if(is(T == short[])) return null; // FIXME!!! 1624 else static if(is(T == int[])) return null; // FIXME!!! 1625 else static if(is(T == long[])) return null; // FIXME!!! 1626 else static if(is(T == float[])) return null; // FIXME!!! 1627 else static if(is(T == double[])) return null; // FIXME!!! 1628 else static if(is(T == E[], E)) return null; // FIXME!!! 1629 1630 1631 else static assert(0, "Unsupported type " ~ T.stringof); 1632 /* // FIXME: finish these. 1633 else static if(is(T == IJavaObject[])) 1634 alias DTypesToJni = jobjectArray; 1635 else static if(is(T == bool[])) 1636 alias DTypesToJni = jbooleanArray; 1637 else static if(is(T == byte[])) 1638 alias DTypesToJni = jbyteArray; 1639 else static if(is(T == wchar[])) 1640 alias DTypesToJni = jcharArray; 1641 else static if(is(T == short[])) 1642 alias DTypesToJni = jshortArray; 1643 else static if(is(T == int[])) 1644 alias DTypesToJni = jintArray; 1645 else static if(is(T == long[])) 1646 alias DTypesToJni = jlongArray; 1647 else static if(is(T == float[])) 1648 alias DTypesToJni = jfloatArray; 1649 else static if(is(T == double[])) 1650 alias DTypesToJni = jdoubleArray; 1651 */ 1652 1653 } 1654 1655 private struct JavaParamsToD(Spec...) { 1656 import std.meta; 1657 1658 Spec args; 1659 AliasSeq!(DTypesToJni!Spec) jargs; 1660 JNIEnv* env; 1661 1662 ~this() { 1663 // import core.stdc.stdio; printf("dtor\n"); 1664 1665 // any time we sliced the Java object directly, we need to clean it up 1666 // so this must stay in sync with the constructor's logic 1667 foreach(idx, arg; args) { 1668 static if(is(typeof(arg) == wstring)) { 1669 // also need to check for null. not allowed to release null 1670 if(arg.ptr !is null) { 1671 auto jarg = jargs[idx]; 1672 (*env).ReleaseStringChars(env, jarg, arg.ptr); 1673 } 1674 } 1675 } 1676 } 1677 1678 this(JNIEnv* env, AliasSeq!(DTypesToJni!Spec) jargs) { 1679 this.jargs = jargs; 1680 this.env = env; 1681 1682 foreach(idx, ref arg; args) { 1683 auto jarg = jargs[idx]; 1684 alias T = typeof(arg); 1685 alias J = typeof(jarg); 1686 1687 static if(__traits(compiles, arg = jarg)) 1688 arg = jarg; 1689 else static if(is(T == size_t)) { 1690 static assert(is(J == jsize)); 1691 arg = cast(size_t) jarg; 1692 } else static if(is(T == string) || is(T == wstring)) { 1693 static assert(is(J == jstring)); 1694 auto len = (*env).GetStringLength(env, jarg); 1695 auto ptr = (*env).GetStringChars(env, jarg, null); 1696 // java strings are immutable so this should be fine 1697 // just remember the lifetime limitation... which is also 1698 // why i am ok 1699 static if(is(T == wstring)) { 1700 if(ptr !is null) 1701 arg = ptr[0 .. len]; 1702 } else { 1703 /* 1704 // I actually can't do this little buffer here 1705 // because this helper function will return before 1706 // it is used. yikes. 1707 char[1024] buffer; 1708 int blen; 1709 if(len < buffer.length / 4) { 1710 foreach(char c; ptr[0 .. len]) 1711 buffer[blen++] = c; 1712 arg = buffer[0 .. blen]; 1713 } else { 1714 arg = to!string(ptr[0 .. len]); 1715 } 1716 */ 1717 import std.conv; 1718 if(ptr !is null) { 1719 arg = to!string(ptr[0 .. len]); 1720 (*env).ReleaseStringChars(env, jarg, ptr); 1721 } 1722 } 1723 // FIXME other types of arrays 1724 } else static if(is(T : IJavaObject)) { 1725 arg = fromExistingJavaObject!T(jarg); 1726 } 1727 else static assert(0, "Unimplemented/unsupported type " ~ T.stringof); 1728 1729 } 1730 } 1731 } 1732 1733 void jniRethrow(JNIEnv* env, Throwable t) { 1734 (*env).ThrowNew( 1735 env, 1736 (*env).FindClass(env, "java/lang/RuntimeException"), 1737 (t.toString() ~ "\0").ptr 1738 ); 1739 } 1740 1741 private mixin template JavaExportImpl(T, alias method, size_t overloadIndex) { 1742 import std.traits; 1743 import std.string; 1744 1745 static if(__traits(identifier, method) == "__ctor") 1746 static assert(0, "Cannot export D constructors"); 1747 1748 extern(System) 1749 private static DTypesToJni!(ReturnType!method) privateJniImplementation(JNIEnv* env, jobject obj, DTypesToJni!(Parameters!method) args) { 1750 // set it up in the thread for future calls 1751 ActivateJniEnv thing = ActivateJniEnv(env); 1752 1753 static if(__traits(isStaticFunction, method)) { 1754 alias dobj = T; 1755 jclass jc = obj; 1756 } else { 1757 // FIXME: pull the same D object again if possible... though idk 1758 ubyte[__traits(classInstanceSize, T)] byteBuffer; 1759 byteBuffer[] = (cast(const(ubyte)[]) typeid(T).initializer())[]; 1760 1761 // I specifically do NOT call the constructor here, since those may forward to Java and make things ugly! 1762 // The init value is cool as-is. 1763 1764 auto dobj = cast(T) byteBuffer.ptr; 1765 dobj.internalJavaHandle_ = obj; 1766 } 1767 1768 // getMember(identifer) is weird but i want to get the method on this 1769 // particular instance and it feels less hacky than doing the delegate 1770 1771 static if(is(typeof(return) == void)) { 1772 try { 1773 __traits(getOverloads, dobj, __traits(identifier, method))[overloadIndex](JavaParamsToD!(Parameters!method)(env, args).args); 1774 } catch(Throwable t) { 1775 jniRethrow(env, t); 1776 } 1777 } else { 1778 try { 1779 return DDatumToJni(env, __traits(getOverloads, dobj, __traits(identifier, method))[overloadIndex](JavaParamsToD!(Parameters!method)(env, args).args)); 1780 } catch(Throwable t) { 1781 jniRethrow(env, t); 1782 return typeof(return).init; // still required to return... 1783 } 1784 } 1785 } 1786 1787 1788 shared static this() { 1789 T.nativeMethodsData_ ~= JNINativeMethod( 1790 getJavaName!method.ptr, 1791 ("(" ~ DTypesToJniString!(Parameters!method) ~ ")" ~ DTypesToJniString!(ReturnType!method) ~ "\0").ptr, 1792 &privateJniImplementation 1793 ); 1794 } 1795 } 1796 1797 /++ 1798 This is really used by the [JavaClass] class below to give a base for all Java classes. 1799 You can use it for that too, but you really shouldn't try to implement it yourself 1800 (it doesn't do much anyway and the other code in here assumes the presence of IJavaObject 1801 on an object also means various internal static members of JavaClass are present too). 1802 +/ 1803 interface IJavaObject { 1804 /// Remember the returned object is a TEMPORARY local reference! 1805 protected jobject getJavaHandle(); 1806 1807 enum Import; /// UDA to indicate you are importing the method from Java. Do NOT put a body on these methods. Only put these on implementation classes, not interfaces. 1808 enum Export; /// UDA to indicate you are exporting the method to Java. Put a D implementation body on these. Only put these on implementation classes, not interfaces. 1809 } 1810 1811 string javaObjectToString(IJavaObject i) { 1812 return "FIXME"; 1813 } 1814 1815 T as(T, R)(R obj) { 1816 // FIXME: this will have to do downcasts to interfaces 1817 return T.init; 1818 } 1819 1820 1821 static T fromExistingJavaObject(T)(jobject o) if(is(T : IJavaObject) && !is(T == interface)) { 1822 import core.memory; 1823 auto ptr = GC.malloc(__traits(classInstanceSize, T)); 1824 ptr[0 .. __traits(classInstanceSize, T)] = typeid(T).initializer[]; 1825 auto obj = cast(T) ptr; 1826 obj.internalJavaHandle_ = o; 1827 return obj; 1828 } 1829 1830 static auto fromExistingJavaObject(T)(jobject o) if(is(T == interface)) { 1831 import std.traits; 1832 static class Dummy : T { 1833 static foreach(memberName; __traits(allMembers, T)) { 1834 static foreach(idx, overload; __traits(getOverloads, T, memberName)) 1835 static if(!__traits(isStaticFunction, overload)) 1836 static foreach(attr; __traits(getAttributes, overload)) { 1837 //static if(!__traits(isStaticFunction, __traits(getMember, T, memberName))) 1838 //static foreach(attr; __traits(getAttributes, __traits(getMember, T, memberName))) { 1839 static if(is(attr == IJavaObject.Import)) { 1840 //mixin("@Import override ReturnType!(__traits(getMember, T, memberName)) " ~ memberName ~ "(Parameters!(__traits(getMember, T, memberName)));"); 1841 mixin("@Import override ReturnType!overload " ~ memberName ~ "(Parameters!overload);"); 1842 } 1843 } 1844 } 1845 1846 mixin IJavaObjectImplementation!(false); 1847 1848 static if(!__traits(compiles, T._javaParameterString)) 1849 mixin JavaPackageId!("java.lang", "Object"); 1850 } 1851 JavaBridge!Dummy bridge; // just to instantiate the impl template 1852 return fromExistingJavaObject!Dummy(o); 1853 } 1854 1855 1856 mixin template ImportExportImpl(Class) if(is(Class == class)) { 1857 static import arsd.jni; 1858 private static arsd.jni.JavaBridge!(Class) _javaDBridge; 1859 } 1860 1861 mixin template ImportExportImpl(Interface) if(is(Interface == interface)) { 1862 static import arsd.jni; 1863 private static arsd.jni.JavaBridgeForInterface!(Interface) _javaDBridge; 1864 } 1865 1866 final class JavaBridgeForInterface(Interface) { 1867 // for interfaces, we do need to implement static members, but nothing else 1868 static foreach(memberName; __traits(derivedMembers, Interface)) { 1869 static foreach(oi, overload; __traits(getOverloads, Interface, memberName)) 1870 static if(__traits(isStaticFunction, overload)) 1871 static foreach(attr; __traits(getAttributes, overload)) { 1872 static if(is(attr == IJavaObject.Import)) 1873 mixin JavaImportImpl!(Interface, overload, oi); 1874 } 1875 } 1876 } 1877 1878 final class JavaBridge(Class) { 1879 static foreach(memberName; __traits(derivedMembers, Class)) { 1880 // validations 1881 static if(is(typeof(__traits(getMember, Class, memberName).offsetof))) 1882 static assert(1, "Data members in D on Java classes are not reliable because they cannot be consistently associated back to their corresponding Java classes through JNI without major runtime expense."); // FIXME 1883 else static if(memberName == "__ctor") 1884 static assert("JavaClasses can only be constructed by Java. Try making a constructor in Java, then make an @Import this(args); here."); 1885 1886 // implementations 1887 static foreach(oi, overload; __traits(getOverloads, Class, memberName)) 1888 static foreach(attr; __traits(getAttributes, overload)) { 1889 static if(is(attr == IJavaObject.Import)) 1890 mixin JavaImportImpl!(Class, overload, oi); 1891 else static if(is(attr == IJavaObject.Export)) 1892 mixin JavaExportImpl!(Class, overload, oi); 1893 } 1894 } 1895 } 1896 1897 1898 /++ 1899 This is the base class you inherit from in D classes that represent Java classes. 1900 You can then mark your methods @Import if they are implemented in Java and you want 1901 to call them from D, or @Export if they are implemented in D and want to be called 1902 as a `native` method from Java. 1903 1904 Methods marked without either of these signifiers are not associated with Java. 1905 1906 You should not expect any instance data on these to survive function calls, since 1907 associating it back with Java across calls may be impossible. 1908 +/ 1909 class JavaClass(string javaPackage, CRTP, Parent = void, bool isNewClass = false) : IJavaObject { 1910 1911 static assert(__traits(isFinalClass, CRTP), "Java classes must be final on the D side and " ~ CRTP.stringof ~ " is not"); 1912 1913 /+ 1914 /++ 1915 D constructors on Java objects don't work right, so this is disabled to ensure 1916 you don't try it. However note that you can `@Import` constructors from Java and 1917 create objects in D that way. 1918 +/ 1919 @disable this(){} 1920 +/ 1921 1922 mixin ImportExportImpl!CRTP; 1923 mixin IJavaObjectImplementation!(isNewClass); 1924 mixin JavaPackageId!(javaPackage, CRTP); 1925 } 1926 1927 mixin template JavaInterfaceMembers(string javaName) { 1928 static import arsd.jni; 1929 /*protected*/ static arsd.jni.jclass internalJavaClassHandle_; 1930 static if(javaName !is null) { 1931 static assert(javaName[0] == 'L' && javaName[$-1] == ';'); 1932 static immutable string _javaParameterString = javaName; 1933 } 1934 } 1935 1936 mixin template IJavaObjectImplementation(bool isNewClass) { 1937 static import arsd.jni; 1938 1939 /+ 1940 import arsd.jni : IJavaObjectSeperate; // WTF the FQN in the is expression didn't work 1941 static if(is(typeof(this) : IJavaObjectSeperate!(ImplInterface), ImplInterface)) { 1942 ImplInterface _d_helper_; 1943 override ImplInterface _d_helper() { return _d_helper_; } 1944 override void _d_helper(ImplInterface i) { _d_helper_ = i; } 1945 } 1946 +/ 1947 1948 /+ 1949 static if(is(typeof(this) S == super)) 1950 static foreach(_superInterface; S) 1951 static if(is(_superInterface == interface)) 1952 static if(__traits(compiles, _superInterface.JavaDefaultImplementations)) { 1953 //pragma(msg, "here"); 1954 mixin _superInterface.JavaDefaultImplementations; 1955 } 1956 +/ 1957 1958 /*protected*/ arsd.jni.jobject internalJavaHandle_; 1959 /*protected*/ override arsd.jni.jobject getJavaHandle() { return internalJavaHandle_; } 1960 1961 /*protected*/ static arsd.jni.jclass internalJavaClassHandle_; 1962 __gshared static /*protected*/ /*immutable*/ arsd.jni.JNINativeMethod[] nativeMethodsData_; 1963 protected static int initializeInJvm_(arsd.jni.JNIEnv* env) { 1964 1965 import core.stdc.stdio; 1966 1967 static if(isNewClass) { 1968 static assert(0, "not really implemented"); 1969 auto aje = arsd.jni.ActivateJniEnv(env); 1970 1971 import std.file; 1972 auto bytes = cast(byte[]) read("Test2.class"); 1973 import std.array; 1974 bytes = bytes.replace(cast(byte[]) "Test2", cast(byte[]) "Test3"); 1975 auto loader = arsd.jni.ClassLoader.getSystemClassLoader().getJavaHandle(); 1976 1977 // doesn't actually work on Android, they didn't implement this function :( :( :( 1978 internalJavaClassHandle_ = (*env).DefineClass(env, "wtf/Test3", loader, bytes.ptr, cast(int) bytes.length); 1979 } else { 1980 internalJavaClassHandle_ = (*env).FindClass(env, (_javaParameterString[1 .. $-1] ~ "\0").ptr); 1981 } 1982 1983 if(!internalJavaClassHandle_) { 1984 (*env).ExceptionDescribe(env); 1985 (*env).ExceptionClear(env); 1986 fprintf(stderr, "Cannot %s Java class for %s [%s]\n", isNewClass ? "create".ptr : "find".ptr, typeof(this).stringof.ptr, (_javaParameterString[1 .. $-1] ~ "\0").ptr); 1987 return 1; 1988 } 1989 1990 if(nativeMethodsData_.length) 1991 if((*env).RegisterNatives(env, internalJavaClassHandle_, nativeMethodsData_.ptr, cast(int) nativeMethodsData_.length)) { 1992 (*env).ExceptionDescribe(env); 1993 (*env).ExceptionClear(env); 1994 fprintf(stderr, ("RegisterNatives failed for " ~ typeof(this).stringof ~ "\0")); 1995 return 1; 1996 } 1997 return 0; 1998 } 1999 shared static this() { 2000 static if(isNewClass) 2001 arsd.jni.newClassInitializers_ ~= &initializeInJvm_; 2002 else 2003 arsd.jni.classInitializers_ ~= &initializeInJvm_; 2004 } 2005 } 2006 2007 mixin template JavaPackageId(string javaPackage, CRTP) { 2008 static import std.string; 2009 static if(javaPackage.length) 2010 public static immutable string _javaParameterString = "L" ~ std..string.replace(javaPackage, ".", "/") ~ "/" ~ getJavaName!CRTP ~ ";"; 2011 else 2012 public static immutable string _javaParameterString = "L" ~ getJavaName!CRTP ~ ";"; 2013 } 2014 2015 mixin template JavaPackageId(string javaPackage, string javaClassName) { 2016 static import std.string; 2017 static if(javaPackage.length) 2018 public static immutable string _javaParameterString = "L" ~ std..string.replace(javaPackage, ".", "/") ~ "/" ~ javaClassName ~ ";"; 2019 else 2020 public static immutable string _javaParameterString = "L" ~ javaClassName ~ ";"; 2021 } 2022 2023 2024 2025 __gshared /* immutable */ int function(JNIEnv* env)[] classInitializers_; 2026 __gshared /* immutable */ int function(JNIEnv* env)[] newClassInitializers_; 2027 2028 final class ClassLoader : JavaClass!("java.lang", ClassLoader) { 2029 @Import static ClassLoader getSystemClassLoader(); 2030 } 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 // Mechanically translated <jni.h> header below. 2046 // You can use it yourself if you need low level access to JNI. 2047 2048 2049 2050 import core.stdc.stdarg; 2051 2052 //version (Android): 2053 extern (System): 2054 @system: 2055 nothrow: 2056 @nogc: 2057 2058 alias bool jboolean; 2059 alias byte jbyte; 2060 alias wchar jchar; 2061 alias short jshort; 2062 alias int jint; 2063 alias long jlong; 2064 alias float jfloat; 2065 alias double jdouble; 2066 alias jint jsize; 2067 alias void* jobject; 2068 alias jobject jclass; 2069 alias jobject jstring; 2070 alias jobject jarray; 2071 alias jarray jobjectArray; 2072 alias jarray jbooleanArray; 2073 alias jarray jbyteArray; 2074 alias jarray jcharArray; 2075 alias jarray jshortArray; 2076 alias jarray jintArray; 2077 alias jarray jlongArray; 2078 alias jarray jfloatArray; 2079 alias jarray jdoubleArray; 2080 alias jobject jthrowable; 2081 alias jobject jweak; 2082 alias _jfieldID* jfieldID; 2083 alias _jmethodID* jmethodID; 2084 alias const(JNINativeInterface)* C_JNIEnv; 2085 alias const(JNINativeInterface)* JNIEnv; 2086 alias const(JNIInvokeInterface)* JavaVM; 2087 2088 enum jobjectRefType 2089 { 2090 JNIInvalidRefType = 0, 2091 JNILocalRefType = 1, 2092 JNIGlobalRefType = 2, 2093 JNIWeakGlobalRefType = 3 2094 } 2095 2096 enum JNI_FALSE = 0; 2097 enum JNI_TRUE = 1; 2098 enum JNI_VERSION_1_1 = 0x00010001; 2099 enum JNI_VERSION_1_2 = 0x00010002; 2100 enum JNI_VERSION_1_4 = 0x00010004; 2101 enum JNI_VERSION_1_6 = 0x00010006; 2102 enum JNI_VERSION_1_8 = 0x00010008; 2103 2104 enum JNI_OK = 0; 2105 enum JNI_ERR = -1; 2106 enum JNI_EDETACHED = -2; 2107 enum JNI_EVERSION = -3; 2108 enum JNI_COMMIT = 1; 2109 enum JNI_ABORT = 2; 2110 2111 struct JNINativeMethod 2112 { 2113 const(char)* name; 2114 const(char)* signature; 2115 void* fnPtr; 2116 } 2117 2118 struct JNINativeInterface 2119 { 2120 void* reserved0; 2121 void* reserved1; 2122 void* reserved2; 2123 void* reserved3; 2124 jint function(JNIEnv*) GetVersion; 2125 jclass function(JNIEnv*, const(char)*, jobject, const(jbyte)*, jsize) DefineClass; 2126 jclass function(JNIEnv*, const(char)*) FindClass; 2127 jmethodID function(JNIEnv*, jobject) FromReflectedMethod; 2128 jfieldID function(JNIEnv*, jobject) FromReflectedField; 2129 jobject function(JNIEnv*, jclass, jmethodID, jboolean) ToReflectedMethod; 2130 jclass function(JNIEnv*, jclass) GetSuperclass; 2131 jboolean function(JNIEnv*, jclass, jclass) IsAssignableFrom; 2132 jobject function(JNIEnv*, jclass, jfieldID, jboolean) ToReflectedField; 2133 jint function(JNIEnv*, jthrowable) Throw; 2134 jint function(JNIEnv*, jclass, const(char)*) ThrowNew; 2135 jthrowable function(JNIEnv*) ExceptionOccurred; 2136 void function(JNIEnv*) ExceptionDescribe; 2137 void function(JNIEnv*) ExceptionClear; 2138 void function(JNIEnv*, const(char)*) FatalError; 2139 jint function(JNIEnv*, jint) PushLocalFrame; 2140 jobject function(JNIEnv*, jobject) PopLocalFrame; 2141 jobject function(JNIEnv*, jobject) NewGlobalRef; 2142 void function(JNIEnv*, jobject) DeleteGlobalRef; 2143 void function(JNIEnv*, jobject) DeleteLocalRef; 2144 jboolean function(JNIEnv*, jobject, jobject) IsSameObject; 2145 jobject function(JNIEnv*, jobject) NewLocalRef; 2146 jint function(JNIEnv*, jint) EnsureLocalCapacity; 2147 jobject function(JNIEnv*, jclass) AllocObject; 2148 jobject function(JNIEnv*, jclass, jmethodID, ...) NewObject; 2149 jobject function(JNIEnv*, jclass, jmethodID, va_list) NewObjectV; 2150 jobject function(JNIEnv*, jclass, jmethodID, jvalue*) NewObjectA; 2151 jclass function(JNIEnv*, jobject) GetObjectClass; 2152 jboolean function(JNIEnv*, jobject, jclass) IsInstanceOf; 2153 jmethodID function(JNIEnv*, jclass, const(char)*, const(char)*) GetMethodID; 2154 jobject function(JNIEnv*, jobject, jmethodID, ...) CallObjectMethod; 2155 jobject function(JNIEnv*, jobject, jmethodID, va_list) CallObjectMethodV; 2156 jobject function(JNIEnv*, jobject, jmethodID, jvalue*) CallObjectMethodA; 2157 jboolean function(JNIEnv*, jobject, jmethodID, ...) CallBooleanMethod; 2158 jboolean function(JNIEnv*, jobject, jmethodID, va_list) CallBooleanMethodV; 2159 jboolean function(JNIEnv*, jobject, jmethodID, jvalue*) CallBooleanMethodA; 2160 jbyte function(JNIEnv*, jobject, jmethodID, ...) CallByteMethod; 2161 jbyte function(JNIEnv*, jobject, jmethodID, va_list) CallByteMethodV; 2162 jbyte function(JNIEnv*, jobject, jmethodID, jvalue*) CallByteMethodA; 2163 jchar function(JNIEnv*, jobject, jmethodID, ...) CallCharMethod; 2164 jchar function(JNIEnv*, jobject, jmethodID, va_list) CallCharMethodV; 2165 jchar function(JNIEnv*, jobject, jmethodID, jvalue*) CallCharMethodA; 2166 jshort function(JNIEnv*, jobject, jmethodID, ...) CallShortMethod; 2167 jshort function(JNIEnv*, jobject, jmethodID, va_list) CallShortMethodV; 2168 jshort function(JNIEnv*, jobject, jmethodID, jvalue*) CallShortMethodA; 2169 jint function(JNIEnv*, jobject, jmethodID, ...) CallIntMethod; 2170 jint function(JNIEnv*, jobject, jmethodID, va_list) CallIntMethodV; 2171 jint function(JNIEnv*, jobject, jmethodID, jvalue*) CallIntMethodA; 2172 jlong function(JNIEnv*, jobject, jmethodID, ...) CallLongMethod; 2173 jlong function(JNIEnv*, jobject, jmethodID, va_list) CallLongMethodV; 2174 jlong function(JNIEnv*, jobject, jmethodID, jvalue*) CallLongMethodA; 2175 jfloat function(JNIEnv*, jobject, jmethodID, ...) CallFloatMethod; 2176 jfloat function(JNIEnv*, jobject, jmethodID, va_list) CallFloatMethodV; 2177 jfloat function(JNIEnv*, jobject, jmethodID, jvalue*) CallFloatMethodA; 2178 jdouble function(JNIEnv*, jobject, jmethodID, ...) CallDoubleMethod; 2179 jdouble function(JNIEnv*, jobject, jmethodID, va_list) CallDoubleMethodV; 2180 jdouble function(JNIEnv*, jobject, jmethodID, jvalue*) CallDoubleMethodA; 2181 void function(JNIEnv*, jobject, jmethodID, ...) CallVoidMethod; 2182 void function(JNIEnv*, jobject, jmethodID, va_list) CallVoidMethodV; 2183 void function(JNIEnv*, jobject, jmethodID, jvalue*) CallVoidMethodA; 2184 jobject function(JNIEnv*, jobject, jclass, jmethodID, ...) CallNonvirtualObjectMethod; 2185 jobject function(JNIEnv*, jobject, jclass, jmethodID, va_list) CallNonvirtualObjectMethodV; 2186 jobject function(JNIEnv*, jobject, jclass, jmethodID, jvalue*) CallNonvirtualObjectMethodA; 2187 jboolean function(JNIEnv*, jobject, jclass, jmethodID, ...) CallNonvirtualBooleanMethod; 2188 jboolean function(JNIEnv*, jobject, jclass, jmethodID, va_list) CallNonvirtualBooleanMethodV; 2189 jboolean function(JNIEnv*, jobject, jclass, jmethodID, jvalue*) CallNonvirtualBooleanMethodA; 2190 jbyte function(JNIEnv*, jobject, jclass, jmethodID, ...) CallNonvirtualByteMethod; 2191 jbyte function(JNIEnv*, jobject, jclass, jmethodID, va_list) CallNonvirtualByteMethodV; 2192 jbyte function(JNIEnv*, jobject, jclass, jmethodID, jvalue*) CallNonvirtualByteMethodA; 2193 jchar function(JNIEnv*, jobject, jclass, jmethodID, ...) CallNonvirtualCharMethod; 2194 jchar function(JNIEnv*, jobject, jclass, jmethodID, va_list) CallNonvirtualCharMethodV; 2195 jchar function(JNIEnv*, jobject, jclass, jmethodID, jvalue*) CallNonvirtualCharMethodA; 2196 jshort function(JNIEnv*, jobject, jclass, jmethodID, ...) CallNonvirtualShortMethod; 2197 jshort function(JNIEnv*, jobject, jclass, jmethodID, va_list) CallNonvirtualShortMethodV; 2198 jshort function(JNIEnv*, jobject, jclass, jmethodID, jvalue*) CallNonvirtualShortMethodA; 2199 jint function(JNIEnv*, jobject, jclass, jmethodID, ...) CallNonvirtualIntMethod; 2200 jint function(JNIEnv*, jobject, jclass, jmethodID, va_list) CallNonvirtualIntMethodV; 2201 jint function(JNIEnv*, jobject, jclass, jmethodID, jvalue*) CallNonvirtualIntMethodA; 2202 jlong function(JNIEnv*, jobject, jclass, jmethodID, ...) CallNonvirtualLongMethod; 2203 jlong function(JNIEnv*, jobject, jclass, jmethodID, va_list) CallNonvirtualLongMethodV; 2204 jlong function(JNIEnv*, jobject, jclass, jmethodID, jvalue*) CallNonvirtualLongMethodA; 2205 jfloat function(JNIEnv*, jobject, jclass, jmethodID, ...) CallNonvirtualFloatMethod; 2206 jfloat function(JNIEnv*, jobject, jclass, jmethodID, va_list) CallNonvirtualFloatMethodV; 2207 jfloat function(JNIEnv*, jobject, jclass, jmethodID, jvalue*) CallNonvirtualFloatMethodA; 2208 jdouble function(JNIEnv*, jobject, jclass, jmethodID, ...) CallNonvirtualDoubleMethod; 2209 jdouble function(JNIEnv*, jobject, jclass, jmethodID, va_list) CallNonvirtualDoubleMethodV; 2210 jdouble function(JNIEnv*, jobject, jclass, jmethodID, jvalue*) CallNonvirtualDoubleMethodA; 2211 void function(JNIEnv*, jobject, jclass, jmethodID, ...) CallNonvirtualVoidMethod; 2212 void function(JNIEnv*, jobject, jclass, jmethodID, va_list) CallNonvirtualVoidMethodV; 2213 void function(JNIEnv*, jobject, jclass, jmethodID, jvalue*) CallNonvirtualVoidMethodA; 2214 jfieldID function(JNIEnv*, jclass, const(char)*, const(char)*) GetFieldID; 2215 jobject function(JNIEnv*, jobject, jfieldID) GetObjectField; 2216 jboolean function(JNIEnv*, jobject, jfieldID) GetBooleanField; 2217 jbyte function(JNIEnv*, jobject, jfieldID) GetByteField; 2218 jchar function(JNIEnv*, jobject, jfieldID) GetCharField; 2219 jshort function(JNIEnv*, jobject, jfieldID) GetShortField; 2220 jint function(JNIEnv*, jobject, jfieldID) GetIntField; 2221 jlong function(JNIEnv*, jobject, jfieldID) GetLongField; 2222 jfloat function(JNIEnv*, jobject, jfieldID) GetFloatField; 2223 jdouble function(JNIEnv*, jobject, jfieldID) GetDoubleField; 2224 void function(JNIEnv*, jobject, jfieldID, jobject) SetObjectField; 2225 void function(JNIEnv*, jobject, jfieldID, jboolean) SetBooleanField; 2226 void function(JNIEnv*, jobject, jfieldID, jbyte) SetByteField; 2227 void function(JNIEnv*, jobject, jfieldID, jchar) SetCharField; 2228 void function(JNIEnv*, jobject, jfieldID, jshort) SetShortField; 2229 void function(JNIEnv*, jobject, jfieldID, jint) SetIntField; 2230 void function(JNIEnv*, jobject, jfieldID, jlong) SetLongField; 2231 void function(JNIEnv*, jobject, jfieldID, jfloat) SetFloatField; 2232 void function(JNIEnv*, jobject, jfieldID, jdouble) SetDoubleField; 2233 jmethodID function(JNIEnv*, jclass, const(char)*, const(char)*) GetStaticMethodID; 2234 jobject function(JNIEnv*, jclass, jmethodID, ...) CallStaticObjectMethod; 2235 jobject function(JNIEnv*, jclass, jmethodID, va_list) CallStaticObjectMethodV; 2236 jobject function(JNIEnv*, jclass, jmethodID, jvalue*) CallStaticObjectMethodA; 2237 jboolean function(JNIEnv*, jclass, jmethodID, ...) CallStaticBooleanMethod; 2238 jboolean function(JNIEnv*, jclass, jmethodID, va_list) CallStaticBooleanMethodV; 2239 jboolean function(JNIEnv*, jclass, jmethodID, jvalue*) CallStaticBooleanMethodA; 2240 jbyte function(JNIEnv*, jclass, jmethodID, ...) CallStaticByteMethod; 2241 jbyte function(JNIEnv*, jclass, jmethodID, va_list) CallStaticByteMethodV; 2242 jbyte function(JNIEnv*, jclass, jmethodID, jvalue*) CallStaticByteMethodA; 2243 jchar function(JNIEnv*, jclass, jmethodID, ...) CallStaticCharMethod; 2244 jchar function(JNIEnv*, jclass, jmethodID, va_list) CallStaticCharMethodV; 2245 jchar function(JNIEnv*, jclass, jmethodID, jvalue*) CallStaticCharMethodA; 2246 jshort function(JNIEnv*, jclass, jmethodID, ...) CallStaticShortMethod; 2247 jshort function(JNIEnv*, jclass, jmethodID, va_list) CallStaticShortMethodV; 2248 jshort function(JNIEnv*, jclass, jmethodID, jvalue*) CallStaticShortMethodA; 2249 jint function(JNIEnv*, jclass, jmethodID, ...) CallStaticIntMethod; 2250 jint function(JNIEnv*, jclass, jmethodID, va_list) CallStaticIntMethodV; 2251 jint function(JNIEnv*, jclass, jmethodID, jvalue*) CallStaticIntMethodA; 2252 jlong function(JNIEnv*, jclass, jmethodID, ...) CallStaticLongMethod; 2253 jlong function(JNIEnv*, jclass, jmethodID, va_list) CallStaticLongMethodV; 2254 jlong function(JNIEnv*, jclass, jmethodID, jvalue*) CallStaticLongMethodA; 2255 jfloat function(JNIEnv*, jclass, jmethodID, ...) CallStaticFloatMethod; 2256 jfloat function(JNIEnv*, jclass, jmethodID, va_list) CallStaticFloatMethodV; 2257 jfloat function(JNIEnv*, jclass, jmethodID, jvalue*) CallStaticFloatMethodA; 2258 jdouble function(JNIEnv*, jclass, jmethodID, ...) CallStaticDoubleMethod; 2259 jdouble function(JNIEnv*, jclass, jmethodID, va_list) CallStaticDoubleMethodV; 2260 jdouble function(JNIEnv*, jclass, jmethodID, jvalue*) CallStaticDoubleMethodA; 2261 void function(JNIEnv*, jclass, jmethodID, ...) CallStaticVoidMethod; 2262 void function(JNIEnv*, jclass, jmethodID, va_list) CallStaticVoidMethodV; 2263 void function(JNIEnv*, jclass, jmethodID, jvalue*) CallStaticVoidMethodA; 2264 jfieldID function(JNIEnv*, jclass, const(char)*, const(char)*) GetStaticFieldID; 2265 jobject function(JNIEnv*, jclass, jfieldID) GetStaticObjectField; 2266 jboolean function(JNIEnv*, jclass, jfieldID) GetStaticBooleanField; 2267 jbyte function(JNIEnv*, jclass, jfieldID) GetStaticByteField; 2268 jchar function(JNIEnv*, jclass, jfieldID) GetStaticCharField; 2269 jshort function(JNIEnv*, jclass, jfieldID) GetStaticShortField; 2270 jint function(JNIEnv*, jclass, jfieldID) GetStaticIntField; 2271 jlong function(JNIEnv*, jclass, jfieldID) GetStaticLongField; 2272 jfloat function(JNIEnv*, jclass, jfieldID) GetStaticFloatField; 2273 jdouble function(JNIEnv*, jclass, jfieldID) GetStaticDoubleField; 2274 void function(JNIEnv*, jclass, jfieldID, jobject) SetStaticObjectField; 2275 void function(JNIEnv*, jclass, jfieldID, jboolean) SetStaticBooleanField; 2276 void function(JNIEnv*, jclass, jfieldID, jbyte) SetStaticByteField; 2277 void function(JNIEnv*, jclass, jfieldID, jchar) SetStaticCharField; 2278 void function(JNIEnv*, jclass, jfieldID, jshort) SetStaticShortField; 2279 void function(JNIEnv*, jclass, jfieldID, jint) SetStaticIntField; 2280 void function(JNIEnv*, jclass, jfieldID, jlong) SetStaticLongField; 2281 void function(JNIEnv*, jclass, jfieldID, jfloat) SetStaticFloatField; 2282 void function(JNIEnv*, jclass, jfieldID, jdouble) SetStaticDoubleField; 2283 jstring function(JNIEnv*, const(jchar)*, jsize) NewString; 2284 jsize function(JNIEnv*, jstring) GetStringLength; 2285 const(jchar)* function(JNIEnv*, jstring, jboolean*) GetStringChars; 2286 void function(JNIEnv*, jstring, const(jchar)*) ReleaseStringChars; 2287 jstring function(JNIEnv*, const(char)*) NewStringUTF; 2288 jsize function(JNIEnv*, jstring) GetStringUTFLength; 2289 const(char)* function(JNIEnv*, jstring, jboolean*) GetStringUTFChars; 2290 void function(JNIEnv*, jstring, const(char)*) ReleaseStringUTFChars; 2291 jsize function(JNIEnv*, jarray) GetArrayLength; 2292 jobjectArray function(JNIEnv*, jsize, jclass, jobject) NewObjectArray; 2293 jobject function(JNIEnv*, jobjectArray, jsize) GetObjectArrayElement; 2294 void function(JNIEnv*, jobjectArray, jsize, jobject) SetObjectArrayElement; 2295 jbooleanArray function(JNIEnv*, jsize) NewBooleanArray; 2296 jbyteArray function(JNIEnv*, jsize) NewByteArray; 2297 jcharArray function(JNIEnv*, jsize) NewCharArray; 2298 jshortArray function(JNIEnv*, jsize) NewShortArray; 2299 jintArray function(JNIEnv*, jsize) NewIntArray; 2300 jlongArray function(JNIEnv*, jsize) NewLongArray; 2301 jfloatArray function(JNIEnv*, jsize) NewFloatArray; 2302 jdoubleArray function(JNIEnv*, jsize) NewDoubleArray; 2303 jboolean* function(JNIEnv*, jbooleanArray, jboolean*) GetBooleanArrayElements; 2304 jbyte* function(JNIEnv*, jbyteArray, jboolean*) GetByteArrayElements; 2305 jchar* function(JNIEnv*, jcharArray, jboolean*) GetCharArrayElements; 2306 jshort* function(JNIEnv*, jshortArray, jboolean*) GetShortArrayElements; 2307 jint* function(JNIEnv*, jintArray, jboolean*) GetIntArrayElements; 2308 jlong* function(JNIEnv*, jlongArray, jboolean*) GetLongArrayElements; 2309 jfloat* function(JNIEnv*, jfloatArray, jboolean*) GetFloatArrayElements; 2310 jdouble* function(JNIEnv*, jdoubleArray, jboolean*) GetDoubleArrayElements; 2311 void function(JNIEnv*, jbooleanArray, jboolean*, jint) ReleaseBooleanArrayElements; 2312 void function(JNIEnv*, jbyteArray, jbyte*, jint) ReleaseByteArrayElements; 2313 void function(JNIEnv*, jcharArray, jchar*, jint) ReleaseCharArrayElements; 2314 void function(JNIEnv*, jshortArray, jshort*, jint) ReleaseShortArrayElements; 2315 void function(JNIEnv*, jintArray, jint*, jint) ReleaseIntArrayElements; 2316 void function(JNIEnv*, jlongArray, jlong*, jint) ReleaseLongArrayElements; 2317 void function(JNIEnv*, jfloatArray, jfloat*, jint) ReleaseFloatArrayElements; 2318 void function(JNIEnv*, jdoubleArray, jdouble*, jint) ReleaseDoubleArrayElements; 2319 void function(JNIEnv*, jbooleanArray, jsize, jsize, jboolean*) GetBooleanArrayRegion; 2320 void function(JNIEnv*, jbyteArray, jsize, jsize, jbyte*) GetByteArrayRegion; 2321 void function(JNIEnv*, jcharArray, jsize, jsize, jchar*) GetCharArrayRegion; 2322 void function(JNIEnv*, jshortArray, jsize, jsize, jshort*) GetShortArrayRegion; 2323 void function(JNIEnv*, jintArray, jsize, jsize, jint*) GetIntArrayRegion; 2324 void function(JNIEnv*, jlongArray, jsize, jsize, jlong*) GetLongArrayRegion; 2325 void function(JNIEnv*, jfloatArray, jsize, jsize, jfloat*) GetFloatArrayRegion; 2326 void function(JNIEnv*, jdoubleArray, jsize, jsize, jdouble*) GetDoubleArrayRegion; 2327 void function(JNIEnv*, jbooleanArray, jsize, jsize, const(jboolean)*) SetBooleanArrayRegion; 2328 void function(JNIEnv*, jbyteArray, jsize, jsize, const(jbyte)*) SetByteArrayRegion; 2329 void function(JNIEnv*, jcharArray, jsize, jsize, const(jchar)*) SetCharArrayRegion; 2330 void function(JNIEnv*, jshortArray, jsize, jsize, const(jshort)*) SetShortArrayRegion; 2331 void function(JNIEnv*, jintArray, jsize, jsize, const(jint)*) SetIntArrayRegion; 2332 void function(JNIEnv*, jlongArray, jsize, jsize, const(jlong)*) SetLongArrayRegion; 2333 void function(JNIEnv*, jfloatArray, jsize, jsize, const(jfloat)*) SetFloatArrayRegion; 2334 void function(JNIEnv*, jdoubleArray, jsize, jsize, const(jdouble)*) SetDoubleArrayRegion; 2335 jint function(JNIEnv*, jclass, const(JNINativeMethod)*, jint) RegisterNatives; 2336 jint function(JNIEnv*, jclass) UnregisterNatives; 2337 jint function(JNIEnv*, jobject) MonitorEnter; 2338 jint function(JNIEnv*, jobject) MonitorExit; 2339 jint function(JNIEnv*, JavaVM**) GetJavaVM; 2340 void function(JNIEnv*, jstring, jsize, jsize, jchar*) GetStringRegion; 2341 void function(JNIEnv*, jstring, jsize, jsize, char*) GetStringUTFRegion; 2342 void* function(JNIEnv*, jarray, jboolean*) GetPrimitiveArrayCritical; 2343 void function(JNIEnv*, jarray, void*, jint) ReleasePrimitiveArrayCritical; 2344 const(jchar)* function(JNIEnv*, jstring, jboolean*) GetStringCritical; 2345 void function(JNIEnv*, jstring, const(jchar)*) ReleaseStringCritical; 2346 jweak function(JNIEnv*, jobject) NewWeakGlobalRef; 2347 void function(JNIEnv*, jweak) DeleteWeakGlobalRef; 2348 jboolean function(JNIEnv*) ExceptionCheck; 2349 jobject function(JNIEnv*, void*, jlong) NewDirectByteBuffer; 2350 void* function(JNIEnv*, jobject) GetDirectBufferAddress; 2351 jlong function(JNIEnv*, jobject) GetDirectBufferCapacity; 2352 jobjectRefType function(JNIEnv*, jobject) GetObjectRefType; 2353 } 2354 2355 struct _JNIEnv 2356 { 2357 const(JNINativeInterface)* functions; 2358 } 2359 2360 struct JNIInvokeInterface 2361 { 2362 void* reserved0; 2363 void* reserved1; 2364 void* reserved2; 2365 jint function(JavaVM*) DestroyJavaVM; 2366 jint function(JavaVM*, JNIEnv**, void*) AttachCurrentThread; 2367 jint function(JavaVM*) DetachCurrentThread; 2368 jint function(JavaVM*, void**, jint) GetEnv; 2369 jint function(JavaVM*, JNIEnv**, void*) AttachCurrentThreadAsDaemon; 2370 } 2371 2372 struct _JavaVM 2373 { 2374 const(JNIInvokeInterface)* functions; 2375 } 2376 2377 struct JavaVMAttachArgs 2378 { 2379 jint version_; 2380 const(char)* name; 2381 jobject group; 2382 } 2383 2384 struct JavaVMOption 2385 { 2386 const(char)* optionString; 2387 void* extraInfo; 2388 } 2389 2390 struct JavaVMInitArgs 2391 { 2392 jint version_; 2393 jint nOptions; 2394 JavaVMOption* options; 2395 jboolean ignoreUnrecognized; 2396 } 2397 2398 jint JNI_GetDefaultJavaVMInitArgs(void *args); 2399 jint JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args); 2400 jint JNI_GetCreatedJavaVMs(JavaVM **, jsize, jsize *); 2401 2402 struct _jfieldID; 2403 struct _jmethodID; 2404 2405 union jvalue 2406 { 2407 jboolean z; 2408 jbyte b; 2409 jchar c; 2410 jshort s; 2411 jint i; 2412 jlong j; 2413 jfloat f; 2414 jdouble d; 2415 jobject l; 2416 } 2417 2418 /* 2419 Copyright 2019-2020, Adam D. Ruppe. 2420 Boost license. or whatever. 2421 Most work done in December 2019. 2422 */