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