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