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 Returns: 1142 an opaque object you should hold on to but not actually use. 1143 Its destructor does necessary cleanup tasks to unload the jvm 1144 but otherwise its effect is global. 1145 1146 As long as that object is in scope, you can work with java classes 1147 globally and it will use that jvm's environment and load classes and jars 1148 through the java classpath. 1149 +/ 1150 auto createJvm()() { 1151 version(Windows) 1152 import core.sys.windows.windows; 1153 else 1154 import core.sys.posix.dlfcn; 1155 1156 static struct JVM { 1157 ActivateJniEnv e; 1158 JavaVM* pvm; 1159 void* jvmdll; 1160 1161 @disable this(this); 1162 1163 ~this() { 1164 if(pvm) 1165 (*pvm).DestroyJavaVM(pvm); 1166 activeJvm = null; 1167 1168 version(Windows) { 1169 if(jvmdll) FreeLibrary(jvmdll); 1170 } else { 1171 if(jvmdll) dlclose(jvmdll); 1172 } 1173 } 1174 } 1175 1176 JavaVM* pvm; 1177 JNIEnv* env; 1178 1179 JavaVMInitArgs vm_args; 1180 JavaVMOption[0] options; 1181 1182 //options[0].optionString = "-Djava.compiler=NONE"; /* disable JIT */ 1183 //options[1].optionString = "-verbose:jni"; /* print JNI-related messages */ 1184 //options[1].optionString = `-Djava.class.path=c:\Users\me\program\jni\`; /* user classes */ 1185 //options[2].optionString = `-Djava.library.path=c:\Users\me\program\jdk-13.0.1\lib\`; /* set native library path */ 1186 1187 vm_args.version_ = JNI_VERSION_DESIRED; 1188 vm_args.options = options.ptr; 1189 vm_args.nOptions = cast(int) options.length; 1190 vm_args.ignoreUnrecognized = true; 1191 1192 //import std.process; 1193 //environment["PATH"] = environment["PATH"] ~ `;c:\users\me\program\jdk-13.0.1\bin\server`; 1194 1195 version(Windows) 1196 auto jvmdll = LoadLibraryW("jvm.dll"w.ptr); 1197 else 1198 auto jvmdll = dlopen("libjvm.so", RTLD_LAZY); 1199 1200 if(jvmdll is null) 1201 throw new Exception("no jvm dll"); 1202 1203 version(Windows) 1204 auto fn = cast(typeof(&JNI_CreateJavaVM)) GetProcAddress(jvmdll, "JNI_CreateJavaVM"); 1205 else 1206 auto fn = cast(typeof(&JNI_CreateJavaVM)) dlsym(jvmdll, "JNI_CreateJavaVM"); 1207 1208 if(fn is null) 1209 throw new Exception("no fun"); 1210 1211 auto res = fn(&pvm, cast(void**) &env, &vm_args);//, args); 1212 if(res != JNI_OK) 1213 throw new Exception("create jvm failed"); // FIXME: throw res); 1214 1215 activeJvm = pvm; 1216 1217 return JVM(ActivateJniEnv(env), pvm, jvmdll); 1218 } 1219 1220 version(Windows) 1221 private extern(Windows) bool SetDllDirectoryW(wstring); 1222 1223 1224 @JavaName("Throwable") 1225 final class JavaThrowable : JavaClass!("java.lang", JavaThrowable) { 1226 @Import string getMessage(); 1227 @Import StackTraceElement[] getStackTrace(); 1228 } 1229 1230 final class StackTraceElement : JavaClass!("java.lang", StackTraceElement) { 1231 @Import this(string declaringClass, string methodName, string fileName, int lineNumber); 1232 @Import string getClassName(); 1233 @Import string getFileName(); 1234 @Import int getLineNumber(); 1235 @Import string getMethodName(); 1236 @Import bool isNativeMethod(); 1237 } 1238 1239 private void exceptionCheck(JNIEnv* env) { 1240 if((*env).ExceptionCheck(env)) { 1241 (*env).ExceptionDescribe(env); // prints it to stderr, not that interesting 1242 jthrowable thrown = (*env).ExceptionOccurred(env); 1243 // do I need to free thrown? 1244 (*env).ExceptionClear(env); 1245 1246 // FIXME 1247 throw new Exception("Java threw"); 1248 } 1249 } 1250 1251 E[] translateJavaArray(E)(JNIEnv* env, jarray jarr) { 1252 if(jarr is null) 1253 return null; 1254 auto len = (*env).GetArrayLength(env, jarr); 1255 static if(is(E == int)) { 1256 auto eles = (*env).GetIntArrayElements(env, jarr, null); 1257 auto res = eles[0 .. len].dup; // FIXME: is this dup strictly necessary? I think it is 1258 (*env).ReleaseIntArrayElements(env, jarr, eles, 0); 1259 } else static if(is(E == bool)) { 1260 auto eles = (*env).GetBooleanArrayElements(env, jarr, null); 1261 auto res = eles[0 .. len].dup; 1262 (*env).ReleaseBooleanArrayElements(env, jarr, eles, 0); 1263 } else static if(is(E == long)) { 1264 auto eles = (*env).GetLongArrayElements(env, jarr, null); 1265 auto res = eles[0 .. len].dup; 1266 (*env).ReleaseLongArrayElements(env, jarr, eles, 0); 1267 } else static if(is(E == short)) { 1268 auto eles = (*env).GetShortArrayElements(env, jarr, null); 1269 auto res = eles[0 .. len].dup; 1270 (*env).ReleaseShortArrayElements(env, jarr, eles, 0); 1271 } else static if(is(E == wchar)) { 1272 auto eles = (*env).GetCharArrayElements(env, jarr, null); 1273 auto res = eles[0 .. len].dup; 1274 (*env).ReleaseCharArrayElements(env, jarr, eles, 0); 1275 } else static if(is(E == float)) { 1276 auto eles = (*env).GetFloatArrayElements(env, jarr, null); 1277 auto res = eles[0 .. len].dup; 1278 (*env).ReleaseFloatArrayElements(env, jarr, eles, 0); 1279 } else static if(is(E == double)) { 1280 auto eles = (*env).GetDoubleArrayElements(env, jarr, null); 1281 auto res = eles[0 .. len].dup; 1282 (*env).ReleaseDoubleArrayElements(env, jarr, eles, 0); 1283 } else static if(is(E == byte)) { 1284 auto eles = (*env).GetByteArrayElements(env, jarr, null); 1285 auto res = eles[0 .. len].dup; 1286 (*env).ReleaseByteArrayElements(env, jarr, eles, 0); 1287 } else static if(is(E == string)) { 1288 string[] res; 1289 1290 if(jarr !is null) { 1291 res.length = len; 1292 foreach(idxarr, ref a; res) { 1293 auto ja = (*env).GetObjectArrayElement(env, jarr, cast(int) idxarr); 1294 a = JavaParamsToD!string(env, ja).args[0].idup; 1295 } 1296 } 1297 } else static if(is(E : IJavaObject)) { 1298 typeof(return) res = null; 1299 1300 if(jarr !is null) { 1301 res.length = len; 1302 foreach(idxarr, ref a; res) { 1303 auto ja = (*env).GetObjectArrayElement(env, jarr, cast(int) idxarr); 1304 a = fromExistingJavaObject!E(ja); 1305 } 1306 } 1307 1308 } else static if(true) { 1309 E[] res; // FIXME FIXME 1310 } 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. 1311 1312 return res; 1313 } 1314 1315 private enum ImportImplementationString = q{ 1316 static if(is(typeof(return) == void)) { 1317 (*env).CallSTATICVoidMethod(env, jobj, _jmethodID, DDataToJni(env, args).args); 1318 exceptionCheck(env); 1319 } else static if(is(typeof(return) == string) || is(typeof(return) == wstring)) { 1320 // I can't just use JavaParamsToD here btw because of lifetime worries. 1321 // maybe i should fix it down there though because there is a lot of duplication 1322 1323 auto jret = (*env).CallSTATICObjectMethod(env, jobj, _jmethodID, DDataToJni(env, args).args); 1324 exceptionCheck(env); 1325 1326 typeof(return) ret; 1327 1328 auto len = (*env).GetStringLength(env, jret); 1329 auto ptr = (*env).GetStringChars(env, jret, null); 1330 static if(is(typeof(return) == wstring)) { 1331 if(ptr !is null) { 1332 ret = ptr[0 .. len].idup; 1333 (*env).ReleaseStringChars(env, jret, ptr); 1334 } 1335 } else { 1336 import std.conv; 1337 if(ptr !is null) { 1338 ret = to!string(ptr[0 .. len]); 1339 (*env).ReleaseStringChars(env, jret, ptr); 1340 } 1341 } 1342 1343 return ret; 1344 } else static if(is(typeof(return) == int)) { 1345 auto ret = (*env).CallSTATICIntMethod(env, jobj, _jmethodID, DDataToJni(env, args).args); 1346 exceptionCheck(env); 1347 return ret; 1348 } else static if(is(typeof(return) == short)) { 1349 auto ret = (*env).CallSTATICShortMethod(env, jobj, _jmethodID, DDataToJni(env, args).args); 1350 exceptionCheck(env); 1351 return ret; 1352 } else static if(is(typeof(return) : IJavaObject)) { 1353 auto ret = (*env).CallSTATICObjectMethod(env, jobj, _jmethodID, DDataToJni(env, args).args); 1354 exceptionCheck(env); 1355 return fromExistingJavaObject!(typeof(return))(ret); 1356 } else static if(is(typeof(return) == long)) { 1357 auto ret = (*env).CallSTATICLongMethod(env, jobj, _jmethodID, DDataToJni(env, args).args); 1358 exceptionCheck(env); 1359 return ret; 1360 } else static if(is(typeof(return) == float)) { 1361 auto ret = (*env).CallSTATICFloatMethod(env, jobj, _jmethodID, DDataToJni(env, args).args); 1362 exceptionCheck(env); 1363 return ret; 1364 } else static if(is(typeof(return) == double)) { 1365 auto ret = (*env).CallSTATICDoubleMethod(env, jobj, _jmethodID, DDataToJni(env, args).args); 1366 exceptionCheck(env); 1367 return ret; 1368 } else static if(is(typeof(return) == bool)) { 1369 auto ret = (*env).CallSTATICBooleanMethod(env, jobj, _jmethodID, DDataToJni(env, args).args); 1370 exceptionCheck(env); 1371 return ret; 1372 } else static if(is(typeof(return) == byte)) { 1373 auto ret = (*env).CallSTATICByteMethod(env, jobj, _jmethodID, DDataToJni(env, args).args); 1374 exceptionCheck(env); 1375 return ret; 1376 } else static if(is(typeof(return) == wchar)) { 1377 auto ret = (*env).CallSTATICCharMethod(env, jobj, _jmethodID, DDataToJni(env, args).args); 1378 exceptionCheck(env); 1379 return ret; 1380 } else static if(is(typeof(return) == E[], E)) { 1381 // Java arrays are represented as objects 1382 auto jarr = (*env).CallSTATICObjectMethod(env, jobj, _jmethodID, DDataToJni(env, args).args); 1383 exceptionCheck(env); 1384 1385 auto res = translateJavaArray!E(env, jarr); 1386 return res; 1387 } else { 1388 static assert(0, "Unsupported return type for JNI: " ~ typeof(return).stringof); 1389 //return DDataToJni(env, __traits(getMember, dobj, __traits(identifier, method))(JavaParamsToD!(Parameters!method)(env, args).args)); 1390 } 1391 }; 1392 1393 import std.string; 1394 static immutable ImportImplementationString_static = ImportImplementationString.replace("STATIC", "Static"); 1395 static immutable ImportImplementationString_not = ImportImplementationString.replace("STATIC", ""); 1396 1397 bool isProperty(string[] items...) { 1398 foreach(item; items) 1399 if(item == "@property") 1400 return true; 1401 return false; 1402 } 1403 1404 private mixin template JavaImportImpl(T, alias method, size_t overloadIndex) { 1405 import std.traits; 1406 1407 static if(isProperty(__traits(getFunctionAttributes, method))) { 1408 1409 private static jfieldID _jfieldID; 1410 1411 static if(__traits(isStaticFunction, method)) 1412 pragma(mangle, method.mangleof) 1413 private static ReturnType!method implementation(Parameters!method args) { 1414 auto env = activeEnv; 1415 if(env is null) 1416 throw new Exception("JNI not active in this thread"); 1417 1418 static if(is(typeof(return) == void)) { 1419 static assert(Parameters!method.length == 1, "Java Property setters must take exactly one argument and return void"); 1420 alias FieldType = Parameters!method[0]; 1421 } else { 1422 static assert(Parameters!method.length == 0, "Java Property getters must take no arguments"); 1423 alias FieldType = typeof(return); 1424 } 1425 1426 if(!_jfieldID) { 1427 jclass jc; 1428 if(!T.internalJavaClassHandle_) { 1429 jc = (*env).FindClass(env, (T._javaParameterString[1 .. $-1] ~ "\0").ptr); 1430 if(!jc) 1431 throw new Exception("Cannot find Java class " ~ T._javaParameterString[1 .. $-1]); 1432 T.internalJavaClassHandle_ = jc; 1433 } else { 1434 jc = T.internalJavaClassHandle_; 1435 } 1436 _jfieldID = (*env).GetStaticFieldID(env, jc, 1437 getJavaName!method.ptr, 1438 (DTypesToJniString!(FieldType) ~ "\0").ptr 1439 ); 1440 1441 if(!_jfieldID) 1442 throw new Exception("Cannot find Java static field " ~ T.stringof ~ "." ~ __traits(identifier, method)); 1443 } 1444 1445 auto jobj = T.internalJavaClassHandle_; // for static 1446 1447 static if(is(typeof(return) == void)) { 1448 // setter 1449 static if(is(FieldType == string) || is(FieldType == wstring)) { 1450 (*env).SetStaticObjectField(env, jobj, _jfieldID, DDataToJni(env, args).args); 1451 } else static if(is(FieldType == int)) { 1452 (*env).SetStaticIntField(env, jobj, _jfieldID, DDataToJni(env, args).args); 1453 } else static if(is(FieldType == short)) { 1454 (*env).SetStaticShortField(env, jobj, _jfieldID, DDataToJni(env, args).args); 1455 } else static if(is(FieldType : IJavaObject)) { 1456 (*env).SetStaticObjectField(env, jobj, _jfieldID, DDataToJni(env, args).args); 1457 } else static if(is(FieldType == long)) { 1458 (*env).SetStaticLongField(env, jobj, _jfieldID, DDataToJni(env, args).args); 1459 } else static if(is(FieldType == float)) { 1460 (*env).SetStaticFloatField(env, jobj, _jfieldID, DDataToJni(env, args).args); 1461 } else static if(is(FieldType == double)) { 1462 (*env).SetStaticDoubleField(env, jobj, _jfieldID, DDataToJni(env, args).args); 1463 } else static if(is(FieldType == bool)) { 1464 (*env).SetStaticBooleanField(env, jobj, _jfieldID, DDataToJni(env, args).args); 1465 } else static if(is(FieldType == byte)) { 1466 (*env).SetStaticByteField(env, jobj, _jfieldID, DDataToJni(env, args).args); 1467 } else static if(is(FieldType == wchar)) { 1468 (*env).SetStaticCharField(env, jobj, _jfieldID, DDataToJni(env, args).args); 1469 } else static if(is(FieldType == E[], E)) { 1470 // Java arrays are represented as objects 1471 (*env).SetStaticObjectField(env, jobj, _jfieldID, DDataToJni(env, args).args); 1472 } else { 1473 static assert(0, "Unsupported return type for JNI: " ~ FieldType.stringof); 1474 //return DDataToJni(env, __traits(getMember, dobj, __traits(identifier, method))(JavaParamsToD!(Parameters!method)(env, args).args)); 1475 } 1476 } else { 1477 // getter 1478 static if(is(FieldType == string) || is(FieldType == wstring)) { 1479 // I can't just use JavaParamsToD here btw because of lifetime worries. 1480 // maybe i should fix it down there though because there is a lot of duplication 1481 1482 auto jret = (*env).GetStaticObjectField(env, jobj, _jfieldID); 1483 1484 FieldType ret; 1485 1486 auto len = (*env).GetStringLength(env, jret); 1487 auto ptr = (*env).GetStringChars(env, jret, null); 1488 static if(is(FieldType == wstring)) { 1489 if(ptr !is null) { 1490 ret = ptr[0 .. len].idup; 1491 (*env).ReleaseStringChars(env, jret, ptr); 1492 } 1493 } else { 1494 import std.conv; 1495 if(ptr !is null) { 1496 ret = to!string(ptr[0 .. len]); 1497 (*env).ReleaseStringChars(env, jret, ptr); 1498 } 1499 } 1500 1501 return ret; 1502 } else static if(is(FieldType == int)) { 1503 auto ret = (*env).GetStaticIntField(env, jobj, _jfieldID); 1504 return ret; 1505 } else static if(is(FieldType == short)) { 1506 auto ret = (*env).GetStaticShortField(env, jobj, _jfieldID); 1507 return ret; 1508 } else static if(is(FieldType : IJavaObject)) { 1509 auto ret = (*env).GetStaticObjectField(env, jobj, _jfieldID); 1510 return fromExistingJavaObject!(FieldType)(ret); 1511 } else static if(is(FieldType == long)) { 1512 auto ret = (*env).GetStaticLongField(env, jobj, _jfieldID); 1513 return ret; 1514 } else static if(is(FieldType == float)) { 1515 auto ret = (*env).GetStaticFloatField(env, jobj, _jfieldID); 1516 return ret; 1517 } else static if(is(FieldType == double)) { 1518 auto ret = (*env).GetStaticDoubleField(env, jobj, _jfieldID); 1519 return ret; 1520 } else static if(is(FieldType == bool)) { 1521 auto ret = (*env).GetStaticBooleanField(env, jobj, _jfieldID); 1522 return ret; 1523 } else static if(is(FieldType == byte)) { 1524 auto ret = (*env).GetStaticByteField(env, jobj, _jfieldID); 1525 return ret; 1526 } else static if(is(FieldType == wchar)) { 1527 auto ret = (*env).GetStaticCharField(env, jobj, _jfieldID); 1528 return ret; 1529 } else static if(is(FieldType == E[], E)) { 1530 // Java arrays are represented as objects 1531 auto jarr = (*env).GetStaticObjectField(env, jobj, _jfieldID); 1532 1533 auto res = translateJavaArray!E(env, jarr); 1534 return res; 1535 } else { 1536 static assert(0, "Unsupported return type for JNI: " ~ FieldType.stringof); 1537 //return DDataToJni(env, __traits(getMember, dobj, __traits(identifier, method))(JavaParamsToD!(Parameters!method)(env, args).args)); 1538 } 1539 } 1540 } 1541 1542 else 1543 pragma(mangle, method.mangleof) 1544 private static ReturnType!method implementation(Parameters!method args, T this_) { 1545 auto env = activeEnv; 1546 if(env is null) 1547 throw new Exception("JNI not active in this thread"); 1548 1549 static if(is(typeof(return) == void)) { 1550 static assert(Parameters!method.length == 1, "Java Property setters must take exactly one argument and return void"); 1551 alias FieldType = Parameters!method[0]; 1552 } else { 1553 static assert(Parameters!method.length == 0, "Java Property getters must take no arguments"); 1554 alias FieldType = typeof(return); 1555 } 1556 1557 if(!_jfieldID) { 1558 jclass jc; 1559 if(!T.internalJavaClassHandle_) { 1560 jc = (*env).FindClass(env, (T._javaParameterString[1 .. $-1] ~ "\0").ptr); 1561 if(!jc) 1562 throw new Exception("Cannot find Java class " ~ T._javaParameterString[1 .. $-1]); 1563 T.internalJavaClassHandle_ = jc; 1564 } else { 1565 jc = T.internalJavaClassHandle_; 1566 } 1567 _jfieldID = (*env).GetFieldID(env, jc, 1568 getJavaName!method.ptr, 1569 (DTypesToJniString!(FieldType) ~ "\0").ptr 1570 ); 1571 1572 if(!_jfieldID) 1573 throw new Exception("Cannot find Java field " ~ T.stringof ~ "." ~ __traits(identifier, method)); 1574 } 1575 1576 // auto jobj = T.internalJavaClassHandle_; // for static 1577 auto jobj = this_.getJavaHandle(); 1578 1579 static if(is(typeof(return) == void)) { 1580 // setter 1581 static if(is(FieldType == string) || is(FieldType == wstring)) { 1582 (*env).SetObjectField(env, jobj, _jfieldID, DDataToJni(env, args).args); 1583 } else static if(is(FieldType == int)) { 1584 (*env).SetIntField(env, jobj, _jfieldID, DDataToJni(env, args).args); 1585 } else static if(is(FieldType == short)) { 1586 (*env).SetShortField(env, jobj, _jfieldID, DDataToJni(env, args).args); 1587 } else static if(is(FieldType : IJavaObject)) { 1588 (*env).SetObjectField(env, jobj, _jfieldID, DDataToJni(env, args).args); 1589 } else static if(is(FieldType == long)) { 1590 (*env).SetLongField(env, jobj, _jfieldID, DDataToJni(env, args).args); 1591 } else static if(is(FieldType == float)) { 1592 (*env).SetFloatField(env, jobj, _jfieldID, DDataToJni(env, args).args); 1593 } else static if(is(FieldType == double)) { 1594 (*env).SetDoubleField(env, jobj, _jfieldID, DDataToJni(env, args).args); 1595 } else static if(is(FieldType == bool)) { 1596 (*env).SetBooleanField(env, jobj, _jfieldID, DDataToJni(env, args).args); 1597 } else static if(is(FieldType == byte)) { 1598 (*env).SetByteField(env, jobj, _jfieldID, DDataToJni(env, args).args); 1599 } else static if(is(FieldType == wchar)) { 1600 (*env).SetCharField(env, jobj, _jfieldID, DDataToJni(env, args).args); 1601 } else static if(is(FieldType == E[], E)) { 1602 // Java arrays are represented as objects 1603 (*env).SetObjectField(env, jobj, _jfieldID, DDataToJni(env, args).args); 1604 } else { 1605 static assert(0, "Unsupported return type for JNI: " ~ FieldType.stringof); 1606 //return DDataToJni(env, __traits(getMember, dobj, __traits(identifier, method))(JavaParamsToD!(Parameters!method)(env, args).args)); 1607 } 1608 } else { 1609 // getter 1610 static if(is(FieldType == string) || is(FieldType == wstring)) { 1611 // I can't just use JavaParamsToD here btw because of lifetime worries. 1612 // maybe i should fix it down there though because there is a lot of duplication 1613 1614 auto jret = (*env).GetObjectField(env, jobj, _jfieldID); 1615 1616 FieldType ret; 1617 1618 auto len = (*env).GetStringLength(env, jret); 1619 auto ptr = (*env).GetStringChars(env, jret, null); 1620 static if(is(FieldType == wstring)) { 1621 if(ptr !is null) { 1622 ret = ptr[0 .. len].idup; 1623 (*env).ReleaseStringChars(env, jret, ptr); 1624 } 1625 } else { 1626 import std.conv; 1627 if(ptr !is null) { 1628 ret = to!string(ptr[0 .. len]); 1629 (*env).ReleaseStringChars(env, jret, ptr); 1630 } 1631 } 1632 1633 return ret; 1634 } else static if(is(FieldType == int)) { 1635 auto ret = (*env).GetIntField(env, jobj, _jfieldID); 1636 return ret; 1637 } else static if(is(FieldType == short)) { 1638 auto ret = (*env).GetShortField(env, jobj, _jfieldID); 1639 return ret; 1640 } else static if(is(FieldType : IJavaObject)) { 1641 auto ret = (*env).GetObjectField(env, jobj, _jfieldID); 1642 return fromExistingJavaObject!(FieldType)(ret); 1643 } else static if(is(FieldType == long)) { 1644 auto ret = (*env).GetLongField(env, jobj, _jfieldID); 1645 return ret; 1646 } else static if(is(FieldType == float)) { 1647 auto ret = (*env).GetFloatField(env, jobj, _jfieldID); 1648 return ret; 1649 } else static if(is(FieldType == double)) { 1650 auto ret = (*env).GetDoubleField(env, jobj, _jfieldID); 1651 return ret; 1652 } else static if(is(FieldType == bool)) { 1653 auto ret = (*env).GetBooleanField(env, jobj, _jfieldID); 1654 return ret; 1655 } else static if(is(FieldType == byte)) { 1656 auto ret = (*env).GetByteField(env, jobj, _jfieldID); 1657 return ret; 1658 } else static if(is(FieldType == wchar)) { 1659 auto ret = (*env).GetCharField(env, jobj, _jfieldID); 1660 return ret; 1661 } else static if(is(FieldType == E[], E)) { 1662 // Java arrays are represented as objects 1663 auto jarr = (*env).GetObjectField(env, jobj, _jfieldID); 1664 1665 auto res = translateJavaArray!E(env, jarr); 1666 return res; 1667 } else { 1668 static assert(0, "Unsupported return type for JNI: " ~ FieldType.stringof); 1669 //return DDataToJni(env, __traits(getMember, dobj, __traits(identifier, method))(JavaParamsToD!(Parameters!method)(env, args).args)); 1670 } 1671 } 1672 } 1673 1674 } else { 1675 private static jmethodID _jmethodID; 1676 1677 static if(__traits(identifier, method) == "__ctor") 1678 pragma(mangle, method.mangleof) 1679 private static T implementation(Parameters!method args, T this_) { 1680 auto env = activeEnv; 1681 if(env is null) 1682 throw new Exception("JNI not active in this thread"); 1683 1684 if(!_jmethodID) { 1685 jclass jc; 1686 if(!T.internalJavaClassHandle_) { 1687 jc = (*env).FindClass(env, (T._javaParameterString[1 .. $-1] ~ "\0").ptr); 1688 if(!jc) 1689 throw new Exception("Cannot find Java class " ~ T._javaParameterString[1 .. $-1]); 1690 T.internalJavaClassHandle_ = jc; 1691 } else { 1692 jc = T.internalJavaClassHandle_; 1693 } 1694 static if(args.length == 1 && is(typeof(args[0]) == arsd.jni.Default)) 1695 _jmethodID = (*env).GetMethodID(env, jc, 1696 "<init>", 1697 // java method string is (args)ret 1698 ("()V\0").ptr 1699 ); 1700 else 1701 _jmethodID = (*env).GetMethodID(env, jc, 1702 "<init>", 1703 // java method string is (args)ret 1704 ("(" ~ DTypesToJniString!(typeof(args)) ~ ")V\0").ptr 1705 ); 1706 1707 if(!_jmethodID) 1708 throw new Exception("Cannot find static Java method " ~ T.stringof ~ "." ~ __traits(identifier, method)); 1709 } 1710 1711 static if(args.length == 1 && is(typeof(args[0]) == arsd.jni.Default)) 1712 auto o = (*env).NewObject(env, T.internalJavaClassHandle_, _jmethodID); 1713 else 1714 auto o = (*env).NewObject(env, T.internalJavaClassHandle_, _jmethodID, DDataToJni(env, args).args); 1715 this_.internalJavaHandle_ = o; 1716 return this_; 1717 } 1718 else static if(__traits(isStaticFunction, method)) 1719 pragma(mangle, method.mangleof) 1720 private static ReturnType!method implementation(Parameters!method args) { 1721 auto env = activeEnv; 1722 if(env is null) 1723 throw new Exception("JNI not active in this thread"); 1724 1725 if(!_jmethodID) { 1726 jclass jc; 1727 if(!T.internalJavaClassHandle_) { 1728 jc = (*env).FindClass(env, (T._javaParameterString[1 .. $-1] ~ "\0").ptr); 1729 if(!jc) 1730 throw new Exception("Cannot find Java class " ~ T._javaParameterString[1 .. $-1]); 1731 T.internalJavaClassHandle_ = jc; 1732 } else { 1733 jc = T.internalJavaClassHandle_; 1734 } 1735 _jmethodID = (*env).GetStaticMethodID(env, jc, 1736 getJavaName!method.ptr, 1737 // java method string is (args)ret 1738 ("(" ~ DTypesToJniString!(typeof(args)) ~ ")" ~ DTypesToJniString!(typeof(return)) ~ "\0").ptr 1739 ); 1740 1741 if(!_jmethodID) 1742 throw new Exception("Cannot find static Java method " ~ T.stringof ~ "." ~ __traits(identifier, method)); 1743 } 1744 1745 auto jobj = T.internalJavaClassHandle_; 1746 1747 mixin(ImportImplementationString_static); 1748 } 1749 else 1750 pragma(mangle, method.mangleof) 1751 private static ReturnType!method implementation(Parameters!method args, T this_) { 1752 auto env = activeEnv; 1753 if(env is null) 1754 throw new Exception("JNI not active in this thread"); 1755 1756 auto jobj = this_.getJavaHandle(); 1757 if(!_jmethodID) { 1758 auto jc = (*env).GetObjectClass(env, jobj); 1759 // just a note: jc is an instance of java.lang.Class 1760 // and we could call getName on it to fetch a String to ID it 1761 _jmethodID = (*env).GetMethodID(env, jc, 1762 getJavaName!method.ptr, 1763 // java method string is (args)ret 1764 ("(" ~ DTypesToJniString!(typeof(args)) ~ ")" ~ DTypesToJniString!(typeof(return)) ~ "\0").ptr 1765 ); 1766 1767 if(!_jmethodID) 1768 throw new Exception("Cannot find Java method " ~ T.stringof ~ "." ~ __traits(identifier, method)); 1769 } 1770 1771 mixin(ImportImplementationString_not); 1772 } 1773 } 1774 } 1775 1776 private template DTypesToJniString(Types...) { 1777 static if(Types.length == 0) 1778 enum string DTypesToJniString = ""; 1779 else static if(Types.length == 1) { 1780 alias T = Types[0]; 1781 1782 static if(is(T == void)) 1783 enum string DTypesToJniString = "V"; 1784 else static if(is(T == string)) { 1785 enum string DTypesToJniString = "Ljava/lang/String;"; 1786 } else static if(is(T == wstring)) 1787 enum string DTypesToJniString = "Ljava/lang/String;"; 1788 else static if(is(T == int)) 1789 enum string DTypesToJniString = "I"; 1790 else static if(is(T == bool)) 1791 enum string DTypesToJniString = "Z"; 1792 else static if(is(T == byte)) 1793 enum string DTypesToJniString = "B"; 1794 else static if(is(T == wchar)) 1795 enum string DTypesToJniString = "C"; 1796 else static if(is(T == short)) 1797 enum string DTypesToJniString = "S"; 1798 else static if(is(T == long)) 1799 enum string DTypesToJniString = "J"; 1800 else static if(is(T == float)) 1801 enum string DTypesToJniString = "F"; 1802 else static if(is(T == double)) 1803 enum string DTypesToJniString = "D"; 1804 else static if(is(T == size_t)) 1805 enum string DTypesToJniString = "I"; // possible FIXME... 1806 else static if(is(T == IJavaObject)) 1807 enum string DTypesToJniString = "LObject;"; // FIXME? 1808 else static if(is(T : IJavaObject)) // child of this but a concrete type 1809 enum string DTypesToJniString = T._javaParameterString; 1810 else static if(is(T == E[], E)) 1811 enum string DTypesToJniString = "[" ~ DTypesToJniString!E; 1812 else static assert(0, "Unsupported type for JNI call " ~ T.stringof); 1813 } else { 1814 private string helper() { 1815 string s; 1816 foreach(Type; Types) 1817 s ~= DTypesToJniString!Type; 1818 return s; 1819 } 1820 enum string DTypesToJniString = helper; 1821 } 1822 } 1823 1824 1825 private template DTypesToJni(Types...) { 1826 static if(Types.length == 0) 1827 alias DTypesToJni = Types; 1828 else static if(Types.length == 1) { 1829 alias T = Types[0]; 1830 1831 static if(is(T == void)) 1832 alias DTypesToJni = void; 1833 else static if(is(T == string)) 1834 alias DTypesToJni = jstring; 1835 else static if(is(T == wstring)) 1836 alias DTypesToJni = jstring; 1837 else static if(is(T == int)) 1838 alias DTypesToJni = jint; 1839 else static if(is(T == bool)) 1840 alias DTypesToJni = jboolean; 1841 else static if(is(T == byte)) 1842 alias DTypesToJni = jbyte; 1843 else static if(is(T == wchar)) 1844 alias DTypesToJni = jchar; 1845 else static if(is(T == short)) 1846 alias DTypesToJni = jshort; 1847 else static if(is(T == long)) 1848 alias DTypesToJni = jlong; 1849 else static if(is(T == float)) 1850 alias DTypesToJni = jfloat; 1851 else static if(is(T == double)) 1852 alias DTypesToJni = jdouble; 1853 else static if(is(T == size_t)) 1854 alias DTypesToJni = jsize; 1855 else static if(is(T : IJavaObject)) 1856 alias DTypesToJni = jobject; 1857 else static if(is(T == IJavaObject[])) 1858 alias DTypesToJni = jobjectArray; 1859 else static if(is(T == bool[])) 1860 alias DTypesToJni = jbooleanArray; 1861 else static if(is(T == byte[])) 1862 alias DTypesToJni = jbyteArray; 1863 else static if(is(T == wchar[])) 1864 alias DTypesToJni = jcharArray; 1865 else static if(is(T == short[])) 1866 alias DTypesToJni = jshortArray; 1867 else static if(is(T == int[])) 1868 alias DTypesToJni = jintArray; 1869 else static if(is(T == long[])) 1870 alias DTypesToJni = jlongArray; 1871 else static if(is(T == float[])) 1872 alias DTypesToJni = jfloatArray; 1873 else static if(is(T == double[])) 1874 alias DTypesToJni = jdoubleArray; 1875 else static if(is(T == string[])) // prolly FIXME 1876 alias DTypesToJni = jobjectArray; 1877 else static if(is(T == E[], E)) // FIXME!!!!!!! 1878 alias DTypesToJni = jobjectArray; 1879 else static assert(0, "Unsupported type for JNI: " ~ T.stringof); 1880 } else { 1881 import std.meta; 1882 // FIXME: write about this later if you forget the ! on the final DTypesToJni, dmd 1883 // says "error: recursive template expansion". woof. 1884 alias DTypesToJni = AliasSeq!(DTypesToJni!(Types[0]), DTypesToJni!(Types[1 .. $])); 1885 } 1886 } 1887 1888 auto DDataToJni(T...)(JNIEnv* env, T data) { 1889 import std.meta; 1890 struct Tmp { 1891 AliasSeq!(DTypesToJni!(T)) args; 1892 } 1893 1894 Tmp t; 1895 foreach(idx, ref arg; t.args) 1896 arg = DDatumToJni(env, data[idx]); 1897 return t; 1898 } 1899 1900 auto DDatumToJni(T)(JNIEnv* env, T data) { 1901 static if(is(T == void)) 1902 static assert(0); 1903 else static if(is(T == string)) { 1904 if(data is null) 1905 return null; 1906 wchar[1024] buffer; 1907 const(wchar)[] translated; 1908 if(data.length < 1024) { 1909 size_t len; 1910 foreach(wchar ch; data) 1911 buffer[len++] = ch; 1912 translated = buffer[0 .. len]; 1913 } else { 1914 import std.conv; 1915 translated = to!wstring(data); 1916 } 1917 // Java copies the buffer so it is perfectly fine to return here now 1918 return (*env).NewString(env, translated.ptr, cast(jsize) translated.length); 1919 } else static if(is(T == wstring)) 1920 return (*env).NewString(env, data.ptr, cast(jsize) data.length); 1921 else static if(is(T == int)) return data; 1922 else static if(is(T == bool)) return data; 1923 else static if(is(T == byte)) return data; 1924 else static if(is(T == wchar)) return data; 1925 else static if(is(T == short)) return data; 1926 else static if(is(T == long)) return data; 1927 else static if(is(T == float)) return data; 1928 else static if(is(T == double)) return data; 1929 1930 else static if(is(T == size_t)) return cast(int) data; 1931 else static if(is(T : IJavaObject)) return data is null ? null : data.getJavaHandle(); 1932 1933 1934 else static if(is(T == string[])) { 1935 auto j = (*env).NewObjectArray(env, cast(int) data.length, (*env).FindClass(env, "java/lang/String"), null); 1936 foreach(idx, str; data) 1937 (*env).SetObjectArrayElement(env, j, cast(int) idx, DDatumToJni(env, str)); 1938 return j; 1939 } else static if(is(T == bool[])) { 1940 auto j = (*env).NewBooleanArray(env, cast(jsize) data.length); 1941 (*env).SetBooleanArrayRegion(env, j, 0, cast(jsize) data.length, data.ptr); 1942 return j; 1943 } else static if(is(T == byte[])) { 1944 auto j = (*env).NewByteArray(env, cast(jsize) data.length); 1945 (*env).SetByteArrayRegion(env, j, 0, cast(jsize) data.length, data.ptr); 1946 return j; 1947 } else static if(is(T == wchar[])) { 1948 return DDatumToJni(env, to!string(data)); // FIXME: could prolly be more efficient 1949 } else static if(is(T == short[])) { 1950 auto j = (*env).NewShortArray(env, cast(jsize) data.length); 1951 (*env).SetShortArrayRegion(env, j, 0, cast(jsize) data.length, data.ptr); 1952 return j; 1953 } else static if(is(T == int[])) { 1954 auto j = (*env).NewIntArray(env, cast(jsize) data.length); 1955 (*env).SetIntArrayRegion(env, j, 0, cast(jsize) data.length, data.ptr); 1956 return j; 1957 } else static if(is(T == long[])) { 1958 auto j = (*env).NewLongArray(env, cast(jsize) data.length); 1959 (*env).SetLongArrayRegion(env, j, 0, cast(jsize) data.length, data.ptr); 1960 return j; 1961 } else static if(is(T == float[])) { 1962 auto j = (*env).NewFloatArray(env, cast(jsize) data.length); 1963 (*env).SetFloatArrayRegion(env, j, 0, cast(jsize) data.length, data.ptr); 1964 return j; 1965 } else static if(is(T == double[])) { 1966 auto j = (*env).NewDoubleArray(env, cast(jsize) data.length); 1967 (*env).SetDoubleArrayRegion(env, j, 0, cast(jsize) data.length, data.ptr); 1968 return j; 1969 } else static if(is(T == E[], E)) { 1970 static if(is(E : IJavaObject)) { 1971 static if(is(E == IJavaObject)) 1972 auto handle = (*env).FindClass(env, "java/lang/Object"); 1973 else 1974 auto handle = E.internalJavaClassHandle_; 1975 1976 auto j = (*env).NewObjectArray(env, cast(int) data.length, handle, null); 1977 foreach(idx, str; data) 1978 (*env).SetObjectArrayElement(env, j, cast(int) idx, DDatumToJni(env, str)); 1979 return j; 1980 } else { 1981 static assert(0, "Unsupported array element type " ~ E.stringof); 1982 } 1983 } 1984 else static assert(0, "Unsupported type " ~ T.stringof); 1985 /* // FIXME: finish these. 1986 else static if(is(T == IJavaObject[])) 1987 alias DTypesToJni = jobjectArray; 1988 else static if(is(T == bool[])) 1989 alias DTypesToJni = jbooleanArray; 1990 else static if(is(T == byte[])) 1991 alias DTypesToJni = jbyteArray; 1992 else static if(is(T == wchar[])) 1993 alias DTypesToJni = jcharArray; 1994 else static if(is(T == short[])) 1995 alias DTypesToJni = jshortArray; 1996 else static if(is(T == int[])) 1997 alias DTypesToJni = jintArray; 1998 else static if(is(T == long[])) 1999 alias DTypesToJni = jlongArray; 2000 else static if(is(T == float[])) 2001 alias DTypesToJni = jfloatArray; 2002 else static if(is(T == double[])) 2003 alias DTypesToJni = jdoubleArray; 2004 */ 2005 2006 } 2007 2008 private struct JavaParamsToD(Spec...) { 2009 import std.meta; 2010 2011 Spec args; 2012 AliasSeq!(DTypesToJni!Spec) jargs; 2013 JNIEnv* env; 2014 2015 ~this() { 2016 // import core.stdc.stdio; printf("dtor\n"); 2017 2018 // any time we sliced the Java object directly, we need to clean it up 2019 // so this must stay in sync with the constructor's logic 2020 foreach(idx, arg; args) { 2021 static if(is(typeof(arg) == wstring)) { 2022 // also need to check for null. not allowed to release null 2023 if(arg.ptr !is null) { 2024 auto jarg = jargs[idx]; 2025 (*env).ReleaseStringChars(env, jarg, arg.ptr); 2026 } 2027 } else static if(is(T == IJavaObject[])) { 2028 // FIXME 2029 } else static if(is(T == bool[])) { 2030 if(arg.ptr !is null) { 2031 auto jarg = jargs[idx]; 2032 (*env).ReleaseBooleanArrayElements(env, jarg, arg.ptr, 0); 2033 } 2034 } else static if(is(T == byte[])) { 2035 if(arg.ptr !is null) { 2036 auto jarg = jargs[idx]; 2037 (*env).ReleaseByteArrayElements(env, jarg, arg.ptr, 0); 2038 } 2039 } else static if(is(T == wchar[])) { 2040 // intentionally blank, wstring did it above 2041 } else static if(is(T == short[])) { 2042 if(arg.ptr !is null) { 2043 auto jarg = jargs[idx]; 2044 (*env).ReleaseShortArrayElements(env, jarg, arg.ptr, 0); 2045 } 2046 } else static if(is(T == int[])) { 2047 if(arg.ptr !is null) { 2048 auto jarg = jargs[idx]; 2049 (*env).ReleaseIntArrayElements(env, jarg, arg.ptr, 0); 2050 } 2051 } else static if(is(T == long[])) { 2052 if(arg.ptr !is null) { 2053 auto jarg = jargs[idx]; 2054 (*env).ReleaseLongArrayElements(env, jarg, arg.ptr, 0); 2055 } 2056 } else static if(is(T == float[])) { 2057 if(arg.ptr !is null) { 2058 auto jarg = jargs[idx]; 2059 (*env).ReleaseFloatArrayElements(env, jarg, arg.ptr, 0); 2060 } 2061 } else static if(is(T == double[])) { 2062 if(arg.ptr !is null) { 2063 auto jarg = jargs[idx]; 2064 (*env).ReleaseDoubleArrayElements(env, jarg, arg.ptr, 0); 2065 } 2066 } 2067 } 2068 } 2069 2070 this(JNIEnv* env, AliasSeq!(DTypesToJni!Spec) jargs) { 2071 this.jargs = jargs; 2072 this.env = env; 2073 2074 foreach(idx, ref arg; args) { 2075 auto jarg = jargs[idx]; 2076 alias T = typeof(arg); 2077 alias J = typeof(jarg); 2078 2079 static if(__traits(compiles, arg = jarg)) 2080 arg = jarg; 2081 else static if(is(T == size_t)) { 2082 static assert(is(J == jsize)); 2083 arg = cast(size_t) jarg; 2084 } else static if(is(T == string) || is(T == wstring)) { 2085 static assert(is(J == jstring)); 2086 auto len = (*env).GetStringLength(env, jarg); 2087 auto ptr = (*env).GetStringChars(env, jarg, null); 2088 // java strings are immutable so this should be fine 2089 // just remember the lifetime limitation... which is also 2090 // why i am ok 2091 static if(is(T == wstring)) { 2092 if(ptr !is null) 2093 arg = ptr[0 .. len]; 2094 } else { 2095 /* 2096 // I actually can't do this little buffer here 2097 // because this helper function will return before 2098 // it is used. yikes. 2099 char[1024] buffer; 2100 int blen; 2101 if(len < buffer.length / 4) { 2102 foreach(char c; ptr[0 .. len]) 2103 buffer[blen++] = c; 2104 arg = buffer[0 .. blen]; 2105 } else { 2106 arg = to!string(ptr[0 .. len]); 2107 } 2108 */ 2109 import std.conv; 2110 if(ptr !is null) { 2111 arg = to!string(ptr[0 .. len]); 2112 (*env).ReleaseStringChars(env, jarg, ptr); 2113 } 2114 } 2115 // FIXME other types of arrays 2116 } else static if(is(T : IJavaObject)) { 2117 arg = fromExistingJavaObject!T(jarg); 2118 } else static if(is(T == bool[])) { 2119 if(jarg !is null) { 2120 auto len = (*env).GetArrayLength(env, jarg); 2121 auto ptr = (*env).GetBooleanArrayElements(env, jarg, null); 2122 arg = ptr is null ? null : ptr[0 .. len]; 2123 } 2124 } else static if(is(T == byte[])) { 2125 if(jarg !is null) { 2126 auto len = (*env).GetArrayLength(env, jarg); 2127 auto ptr = (*env).GetByteArrayElements(env, jarg, null); 2128 arg = ptr is null ? null : ptr[0 .. len]; 2129 } 2130 } else static if(is(T == wchar[])) { 2131 // handled above 2132 } else static if(is(T == short[])) { 2133 if(jarg !is null) { 2134 auto len = (*env).GetArrayLength(env, jarg); 2135 auto ptr = (*env).GetShortArrayElements(env, jarg, null); 2136 arg = ptr is null ? null : ptr[0 .. len]; 2137 } 2138 } else static if(is(T == int[])) { 2139 if(jarg !is null) { 2140 auto len = (*env).GetArrayLength(env, jarg); 2141 auto ptr = (*env).GetIntArrayElements(env, jarg, null); 2142 arg = ptr is null ? null : ptr[0 .. len]; 2143 } 2144 } else static if(is(T == long[])) { 2145 if(jarg !is null) { 2146 auto len = (*env).GetArrayLength(env, jarg); 2147 auto ptr = (*env).GetLongArrayElements(env, jarg, null); 2148 arg = ptr is null ? null : ptr[0 .. len]; 2149 } 2150 } else static if(is(T == float[])) { 2151 if(jarg !is null) { 2152 auto len = (*env).GetArrayLength(env, jarg); 2153 auto ptr = (*env).GetFloatArrayElements(env, jarg, null); 2154 arg = ptr is null ? null : ptr[0 .. len]; 2155 } 2156 } else static if(is(T == double[])) { 2157 if(jarg !is null) { 2158 auto len = (*env).GetArrayLength(env, jarg); 2159 auto ptr = (*env).GetDoubleArrayElements(env, jarg, null); 2160 arg = ptr is null ? null : ptr[0 .. len]; 2161 } 2162 } else static if(is(T == string[])) { 2163 if(jarg !is null) { 2164 auto len = (*env).GetArrayLength(env, jarg); 2165 arg.length = len; 2166 foreach(idxarr, ref a; arg) { 2167 auto ja = (*env).GetObjectArrayElement(env, jarg, cast(int) idxarr); 2168 a = JavaParamsToD!string(env, ja).args[0].idup; 2169 } 2170 } 2171 } else static if(is(T == E[], E)) { 2172 static if(is(E : IJavaObject)) { 2173 if(jarg !is null) { 2174 auto len = (*env).GetArrayLength(env, jarg); 2175 arg.length = len; 2176 foreach(idxarr, ref a; arg) { 2177 auto ja = (*env).GetObjectArrayElement(env, jarg, cast(int) idxarr); 2178 a = fromExistingJavaObject!E(ja); 2179 } 2180 } 2181 } else static assert(0, "Unsupported array element type " ~ E.stringof); 2182 // FIXME: actually check the other types not just the generic array 2183 } 2184 else static assert(0, "Unimplemented/unsupported type " ~ T.stringof); 2185 2186 } 2187 } 2188 } 2189 2190 void jniRethrow(JNIEnv* env, Throwable t) { 2191 (*env).ThrowNew( 2192 env, 2193 (*env).FindClass(env, "java/lang/RuntimeException"), 2194 (t.toString() ~ "\0").ptr 2195 ); 2196 } 2197 2198 private mixin template JavaExportImpl(T, alias method, size_t overloadIndex) { 2199 import std.traits; 2200 import std.string; 2201 2202 static if(__traits(identifier, method) == "__ctor") 2203 static assert(0, "Cannot export D constructors"); 2204 2205 extern(System) 2206 private static DTypesToJni!(ReturnType!method) privateJniImplementation(JNIEnv* env, jobject obj, DTypesToJni!(Parameters!method) args) { 2207 // set it up in the thread for future calls 2208 ActivateJniEnv thing = ActivateJniEnv(env); 2209 2210 static if(__traits(isStaticFunction, method)) { 2211 alias dobj = T; 2212 jclass jc = obj; 2213 } else { 2214 // FIXME: pull the same D object again if possible... though idk 2215 ubyte[__traits(classInstanceSize, T)] byteBuffer; 2216 byteBuffer[] = (cast(const(ubyte)[]) typeid(T).initializer())[]; 2217 2218 // I specifically do NOT call the constructor here, since those may forward to Java and make things ugly! 2219 // The init value is cool as-is. 2220 2221 auto dobj = cast(T) byteBuffer.ptr; 2222 dobj.internalJavaHandle_ = obj; 2223 } 2224 2225 // getMember(identifer) is weird but i want to get the method on this 2226 // particular instance and it feels less hacky than doing the delegate 2227 2228 static if(is(typeof(return) == void)) { 2229 try { 2230 __traits(getOverloads, dobj, __traits(identifier, method))[overloadIndex](JavaParamsToD!(Parameters!method)(env, args).args); 2231 } catch(Throwable t) { 2232 jniRethrow(env, t); 2233 } 2234 } else { 2235 try { 2236 return DDatumToJni(env, __traits(getOverloads, dobj, __traits(identifier, method))[overloadIndex](JavaParamsToD!(Parameters!method)(env, args).args)); 2237 } catch(Throwable t) { 2238 jniRethrow(env, t); 2239 return typeof(return).init; // still required to return... 2240 } 2241 } 2242 } 2243 2244 2245 shared static this() { 2246 T.nativeMethodsData_ ~= JNINativeMethod( 2247 getJavaName!method.ptr, 2248 ("(" ~ DTypesToJniString!(Parameters!method) ~ ")" ~ DTypesToJniString!(ReturnType!method) ~ "\0").ptr, 2249 &privateJniImplementation 2250 ); 2251 } 2252 } 2253 2254 /++ 2255 This is really used by the [JavaClass] class below to give a base for all Java classes. 2256 You can use it for that too, but you really shouldn't try to implement it yourself 2257 (it doesn't do much anyway and the other code in here assumes the presence of IJavaObject 2258 on an object also means various internal static members of JavaClass are present too). 2259 +/ 2260 interface IJavaObject { 2261 /// Remember the returned object is a TEMPORARY local reference! 2262 protected jobject getJavaHandle(); 2263 2264 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. 2265 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. 2266 } 2267 2268 string javaObjectToString(IJavaObject i) { 2269 return "FIXME"; 2270 } 2271 2272 T as(T, R)(R obj) { 2273 // FIXME: this will have to do downcasts to interfaces 2274 return T.init; 2275 } 2276 2277 2278 static T fromExistingJavaObject(T)(jobject o) if(is(T : IJavaObject) && !is(T == interface)) { 2279 if(o is null) 2280 return null; 2281 import core.memory; 2282 auto ptr = GC.malloc(__traits(classInstanceSize, T)); 2283 ptr[0 .. __traits(classInstanceSize, T)] = typeid(T).initializer[]; 2284 auto obj = cast(T) ptr; 2285 obj.internalJavaHandle_ = o; 2286 return obj; 2287 } 2288 2289 static auto fromExistingJavaObject(T)(jobject o) if(is(T == interface)) { 2290 import std.traits; 2291 static class Dummy : T { 2292 static foreach(memberName; __traits(allMembers, T)) { 2293 static foreach(idx, overload; __traits(getOverloads, T, memberName)) 2294 static if(!__traits(isStaticFunction, overload)) 2295 static foreach(attr; __traits(getAttributes, overload)) { 2296 //static if(!__traits(isStaticFunction, __traits(getMember, T, memberName))) 2297 //static foreach(attr; __traits(getAttributes, __traits(getMember, T, memberName))) { 2298 static if(is(attr == IJavaObject.Import)) { 2299 //mixin("@Import override ReturnType!(__traits(getMember, T, memberName)) " ~ memberName ~ "(Parameters!(__traits(getMember, T, memberName)));"); 2300 mixin("@Import override ReturnType!overload " ~ memberName ~ "(Parameters!overload);"); 2301 } 2302 } 2303 } 2304 2305 mixin IJavaObjectImplementation!(false); 2306 2307 static if(!__traits(compiles, T._javaParameterString)) 2308 mixin JavaPackageId!("java.lang", "Object"); 2309 } 2310 JavaBridge!Dummy bridge; // just to instantiate the impl template 2311 return fromExistingJavaObject!Dummy(o); 2312 } 2313 2314 2315 mixin template ImportExportImpl(Class) if(is(Class == class)) { 2316 static import arsd.jni; 2317 private static arsd.jni.JavaBridge!(Class) _javaDBridge; 2318 } 2319 2320 mixin template ImportExportImpl(Interface) if(is(Interface == interface)) { 2321 static import arsd.jni; 2322 private static arsd.jni.JavaBridgeForInterface!(Interface) _javaDBridge; 2323 } 2324 2325 final class JavaBridgeForInterface(Interface) { 2326 // for interfaces, we do need to implement static members, but nothing else 2327 static foreach(memberName; __traits(derivedMembers, Interface)) { 2328 static foreach(oi, overload; __traits(getOverloads, Interface, memberName)) 2329 static if(__traits(isStaticFunction, overload)) 2330 static foreach(attr; __traits(getAttributes, overload)) { 2331 static if(is(attr == IJavaObject.Import)) 2332 mixin JavaImportImpl!(Interface, overload, oi); 2333 } 2334 } 2335 } 2336 2337 final class JavaBridge(Class) { 2338 static foreach(memberName; __traits(derivedMembers, Class)) { 2339 // validations 2340 static if(is(typeof(__traits(getMember, Class, memberName).offsetof))) 2341 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 2342 else static if(memberName == "__ctor") 2343 static assert("JavaClasses can only be constructed by Java. Try making a constructor in Java, then make an @Import this(args); here."); 2344 2345 // implementations 2346 static foreach(oi, overload; __traits(getOverloads, Class, memberName)) 2347 static foreach(attr; __traits(getAttributes, overload)) { 2348 static if(is(attr == IJavaObject.Import)) 2349 mixin JavaImportImpl!(Class, overload, oi); 2350 else static if(is(attr == IJavaObject.Export)) 2351 mixin JavaExportImpl!(Class, overload, oi); 2352 } 2353 } 2354 } 2355 2356 2357 /++ 2358 This is the base class you inherit from in D classes that represent Java classes. 2359 You can then mark your methods @Import if they are implemented in Java and you want 2360 to call them from D, or @Export if they are implemented in D and want to be called 2361 as a `native` method from Java. 2362 2363 Methods marked without either of these signifiers are not associated with Java. 2364 2365 You should not expect any instance data on these to survive function calls, since 2366 associating it back with Java across calls may be impossible. 2367 +/ 2368 class JavaClass(string javaPackage, CRTP, Parent = void, bool isNewClass = false) : IJavaObject { 2369 2370 static assert(__traits(isFinalClass, CRTP), "Java classes must be final on the D side and " ~ CRTP.stringof ~ " is not"); 2371 2372 /+ 2373 /++ 2374 D constructors on Java objects don't work right, so this is disabled to ensure 2375 you don't try it. However note that you can `@Import` constructors from Java and 2376 create objects in D that way. 2377 +/ 2378 @disable this(){} 2379 +/ 2380 2381 mixin ImportExportImpl!CRTP; 2382 mixin IJavaObjectImplementation!(isNewClass); 2383 mixin JavaPackageId!(javaPackage, CRTP); 2384 } 2385 2386 mixin template JavaInterfaceMembers(string javaName) { 2387 static import arsd.jni; 2388 /*protected*/ static arsd.jni.jclass internalJavaClassHandle_; 2389 static if(javaName !is null) { 2390 static assert(javaName[0] == 'L' && javaName[$-1] == ';'); 2391 static immutable string _javaParameterString = javaName; 2392 } 2393 } 2394 2395 mixin template IJavaObjectImplementation(bool isNewClass) { 2396 static import arsd.jni; 2397 2398 /+ 2399 import arsd.jni : IJavaObjectSeperate; // WTF the FQN in the is expression didn't work 2400 static if(is(typeof(this) : IJavaObjectSeperate!(ImplInterface), ImplInterface)) { 2401 ImplInterface _d_helper_; 2402 override ImplInterface _d_helper() { return _d_helper_; } 2403 override void _d_helper(ImplInterface i) { _d_helper_ = i; } 2404 } 2405 +/ 2406 2407 /+ 2408 static if(is(typeof(this) S == super)) 2409 static foreach(_superInterface; S) 2410 static if(is(_superInterface == interface)) 2411 static if(__traits(compiles, _superInterface.JavaDefaultImplementations)) { 2412 //pragma(msg, "here"); 2413 mixin _superInterface.JavaDefaultImplementations; 2414 } 2415 +/ 2416 2417 /*protected*/ arsd.jni.jobject internalJavaHandle_; 2418 /*protected*/ override arsd.jni.jobject getJavaHandle() { return internalJavaHandle_; } 2419 2420 /*protected*/ static arsd.jni.jclass internalJavaClassHandle_; 2421 __gshared static /*protected*/ /*immutable*/ arsd.jni.JNINativeMethod[] nativeMethodsData_; 2422 protected static int initializeInJvm_(arsd.jni.JNIEnv* env) { 2423 2424 import core.stdc.stdio; 2425 2426 static if(isNewClass) { 2427 static assert(0, "not really implemented"); 2428 auto aje = arsd.jni.ActivateJniEnv(env); 2429 2430 import std.file; 2431 auto bytes = cast(byte[]) read("Test2.class"); 2432 import std.array; 2433 bytes = bytes.replace(cast(byte[]) "Test2", cast(byte[]) "Test3"); 2434 auto loader = arsd.jni.ClassLoader.getSystemClassLoader().getJavaHandle(); 2435 2436 // doesn't actually work on Android, they didn't implement this function :( :( :( 2437 internalJavaClassHandle_ = (*env).DefineClass(env, "wtf/Test3", loader, bytes.ptr, cast(int) bytes.length); 2438 } else { 2439 internalJavaClassHandle_ = (*env).FindClass(env, (_javaParameterString[1 .. $-1] ~ "\0").ptr); 2440 } 2441 2442 if(!internalJavaClassHandle_) { 2443 (*env).ExceptionDescribe(env); 2444 (*env).ExceptionClear(env); 2445 fprintf(stderr, "Cannot %s Java class for %s [%s]\n", isNewClass ? "create".ptr : "find".ptr, typeof(this).stringof.ptr, (_javaParameterString[1 .. $-1] ~ "\0").ptr); 2446 return 1; 2447 } 2448 2449 if(nativeMethodsData_.length) 2450 if((*env).RegisterNatives(env, internalJavaClassHandle_, nativeMethodsData_.ptr, cast(int) nativeMethodsData_.length)) { 2451 (*env).ExceptionDescribe(env); 2452 (*env).ExceptionClear(env); 2453 fprintf(stderr, ("RegisterNatives failed for " ~ typeof(this).stringof ~ "\0")); 2454 return 1; 2455 } 2456 return 0; 2457 } 2458 shared static this() { 2459 static if(isNewClass) 2460 arsd.jni.newClassInitializers_ ~= &initializeInJvm_; 2461 else 2462 arsd.jni.classInitializers_ ~= &initializeInJvm_; 2463 } 2464 } 2465 2466 mixin template JavaPackageId(string javaPackage, CRTP) { 2467 static import std.string; 2468 static if(javaPackage.length) 2469 public static immutable string _javaParameterString = "L" ~ std..string.replace(javaPackage, ".", "/") ~ "/" ~ getJavaName!CRTP ~ ";"; 2470 else 2471 public static immutable string _javaParameterString = "L" ~ getJavaName!CRTP ~ ";"; 2472 } 2473 2474 mixin template JavaPackageId(string javaPackage, string javaClassName) { 2475 static import std.string; 2476 static if(javaPackage.length) 2477 public static immutable string _javaParameterString = "L" ~ std..string.replace(javaPackage, ".", "/") ~ "/" ~ javaClassName ~ ";"; 2478 else 2479 public static immutable string _javaParameterString = "L" ~ javaClassName ~ ";"; 2480 } 2481 2482 2483 2484 __gshared /* immutable */ int function(JNIEnv* env)[] classInitializers_; 2485 __gshared /* immutable */ int function(JNIEnv* env)[] newClassInitializers_; 2486 2487 final class ClassLoader : JavaClass!("java.lang", ClassLoader) { 2488 @Import static ClassLoader getSystemClassLoader(); 2489 } 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 // Mechanically translated <jni.h> header below. 2505 // You can use it yourself if you need low level access to JNI. 2506 2507 2508 2509 import core.stdc.stdarg; 2510 2511 //version (Android): 2512 extern (System): 2513 @system: 2514 nothrow: 2515 @nogc: 2516 2517 alias bool jboolean; 2518 alias byte jbyte; 2519 alias wchar jchar; 2520 alias short jshort; 2521 alias int jint; 2522 alias long jlong; 2523 alias float jfloat; 2524 alias double jdouble; 2525 alias jint jsize; 2526 alias void* jobject; 2527 alias jobject jclass; 2528 alias jobject jstring; 2529 alias jobject jarray; 2530 alias jarray jobjectArray; 2531 alias jarray jbooleanArray; 2532 alias jarray jbyteArray; 2533 alias jarray jcharArray; 2534 alias jarray jshortArray; 2535 alias jarray jintArray; 2536 alias jarray jlongArray; 2537 alias jarray jfloatArray; 2538 alias jarray jdoubleArray; 2539 alias jobject jthrowable; 2540 alias jobject jweak; 2541 alias _jfieldID* jfieldID; 2542 alias _jmethodID* jmethodID; 2543 alias const(JNINativeInterface)* C_JNIEnv; 2544 alias const(JNINativeInterface)* JNIEnv; 2545 alias const(JNIInvokeInterface)* JavaVM; 2546 2547 enum jobjectRefType 2548 { 2549 JNIInvalidRefType = 0, 2550 JNILocalRefType = 1, 2551 JNIGlobalRefType = 2, 2552 JNIWeakGlobalRefType = 3 2553 } 2554 2555 enum JNI_FALSE = 0; 2556 enum JNI_TRUE = 1; 2557 enum JNI_VERSION_1_1 = 0x00010001; 2558 enum JNI_VERSION_1_2 = 0x00010002; 2559 enum JNI_VERSION_1_4 = 0x00010004; 2560 enum JNI_VERSION_1_6 = 0x00010006; 2561 enum JNI_VERSION_1_8 = 0x00010008; 2562 enum JNI_VERSION_9 = 0x00090000; 2563 enum JNI_VERSION_10 = 0x000a0000; 2564 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 2565 2566 enum JNI_OK = 0; 2567 enum JNI_ERR = -1; 2568 enum JNI_EDETACHED = -2; 2569 enum JNI_EVERSION = -3; 2570 enum JNI_COMMIT = 1; 2571 enum JNI_ABORT = 2; 2572 2573 struct JNINativeMethod 2574 { 2575 const(char)* name; 2576 const(char)* signature; 2577 void* fnPtr; 2578 } 2579 2580 struct JNINativeInterface 2581 { 2582 void* reserved0; 2583 void* reserved1; 2584 void* reserved2; 2585 void* reserved3; 2586 jint function(JNIEnv*) GetVersion; 2587 jclass function(JNIEnv*, const(char)*, jobject, const(jbyte)*, jsize) DefineClass; 2588 jclass function(JNIEnv*, const(char)*) FindClass; 2589 jmethodID function(JNIEnv*, jobject) FromReflectedMethod; 2590 jfieldID function(JNIEnv*, jobject) FromReflectedField; 2591 jobject function(JNIEnv*, jclass, jmethodID, jboolean) ToReflectedMethod; 2592 jclass function(JNIEnv*, jclass) GetSuperclass; 2593 jboolean function(JNIEnv*, jclass, jclass) IsAssignableFrom; 2594 jobject function(JNIEnv*, jclass, jfieldID, jboolean) ToReflectedField; 2595 jint function(JNIEnv*, jthrowable) Throw; 2596 jint function(JNIEnv*, jclass, const(char)*) ThrowNew; 2597 jthrowable function(JNIEnv*) ExceptionOccurred; 2598 void function(JNIEnv*) ExceptionDescribe; 2599 void function(JNIEnv*) ExceptionClear; 2600 void function(JNIEnv*, const(char)*) FatalError; 2601 jint function(JNIEnv*, jint) PushLocalFrame; 2602 jobject function(JNIEnv*, jobject) PopLocalFrame; 2603 jobject function(JNIEnv*, jobject) NewGlobalRef; 2604 void function(JNIEnv*, jobject) DeleteGlobalRef; 2605 void function(JNIEnv*, jobject) DeleteLocalRef; 2606 jboolean function(JNIEnv*, jobject, jobject) IsSameObject; 2607 jobject function(JNIEnv*, jobject) NewLocalRef; 2608 jint function(JNIEnv*, jint) EnsureLocalCapacity; 2609 jobject function(JNIEnv*, jclass) AllocObject; 2610 jobject function(JNIEnv*, jclass, jmethodID, ...) NewObject; 2611 jobject function(JNIEnv*, jclass, jmethodID, va_list) NewObjectV; 2612 jobject function(JNIEnv*, jclass, jmethodID, jvalue*) NewObjectA; 2613 jclass function(JNIEnv*, jobject) GetObjectClass; 2614 jboolean function(JNIEnv*, jobject, jclass) IsInstanceOf; 2615 jmethodID function(JNIEnv*, jclass, const(char)*, const(char)*) GetMethodID; 2616 jobject function(JNIEnv*, jobject, jmethodID, ...) CallObjectMethod; 2617 jobject function(JNIEnv*, jobject, jmethodID, va_list) CallObjectMethodV; 2618 jobject function(JNIEnv*, jobject, jmethodID, jvalue*) CallObjectMethodA; 2619 jboolean function(JNIEnv*, jobject, jmethodID, ...) CallBooleanMethod; 2620 jboolean function(JNIEnv*, jobject, jmethodID, va_list) CallBooleanMethodV; 2621 jboolean function(JNIEnv*, jobject, jmethodID, jvalue*) CallBooleanMethodA; 2622 jbyte function(JNIEnv*, jobject, jmethodID, ...) CallByteMethod; 2623 jbyte function(JNIEnv*, jobject, jmethodID, va_list) CallByteMethodV; 2624 jbyte function(JNIEnv*, jobject, jmethodID, jvalue*) CallByteMethodA; 2625 jchar function(JNIEnv*, jobject, jmethodID, ...) CallCharMethod; 2626 jchar function(JNIEnv*, jobject, jmethodID, va_list) CallCharMethodV; 2627 jchar function(JNIEnv*, jobject, jmethodID, jvalue*) CallCharMethodA; 2628 jshort function(JNIEnv*, jobject, jmethodID, ...) CallShortMethod; 2629 jshort function(JNIEnv*, jobject, jmethodID, va_list) CallShortMethodV; 2630 jshort function(JNIEnv*, jobject, jmethodID, jvalue*) CallShortMethodA; 2631 jint function(JNIEnv*, jobject, jmethodID, ...) CallIntMethod; 2632 jint function(JNIEnv*, jobject, jmethodID, va_list) CallIntMethodV; 2633 jint function(JNIEnv*, jobject, jmethodID, jvalue*) CallIntMethodA; 2634 jlong function(JNIEnv*, jobject, jmethodID, ...) CallLongMethod; 2635 jlong function(JNIEnv*, jobject, jmethodID, va_list) CallLongMethodV; 2636 jlong function(JNIEnv*, jobject, jmethodID, jvalue*) CallLongMethodA; 2637 jfloat function(JNIEnv*, jobject, jmethodID, ...) CallFloatMethod; 2638 jfloat function(JNIEnv*, jobject, jmethodID, va_list) CallFloatMethodV; 2639 jfloat function(JNIEnv*, jobject, jmethodID, jvalue*) CallFloatMethodA; 2640 jdouble function(JNIEnv*, jobject, jmethodID, ...) CallDoubleMethod; 2641 jdouble function(JNIEnv*, jobject, jmethodID, va_list) CallDoubleMethodV; 2642 jdouble function(JNIEnv*, jobject, jmethodID, jvalue*) CallDoubleMethodA; 2643 void function(JNIEnv*, jobject, jmethodID, ...) CallVoidMethod; 2644 void function(JNIEnv*, jobject, jmethodID, va_list) CallVoidMethodV; 2645 void function(JNIEnv*, jobject, jmethodID, jvalue*) CallVoidMethodA; 2646 jobject function(JNIEnv*, jobject, jclass, jmethodID, ...) CallNonvirtualObjectMethod; 2647 jobject function(JNIEnv*, jobject, jclass, jmethodID, va_list) CallNonvirtualObjectMethodV; 2648 jobject function(JNIEnv*, jobject, jclass, jmethodID, jvalue*) CallNonvirtualObjectMethodA; 2649 jboolean function(JNIEnv*, jobject, jclass, jmethodID, ...) CallNonvirtualBooleanMethod; 2650 jboolean function(JNIEnv*, jobject, jclass, jmethodID, va_list) CallNonvirtualBooleanMethodV; 2651 jboolean function(JNIEnv*, jobject, jclass, jmethodID, jvalue*) CallNonvirtualBooleanMethodA; 2652 jbyte function(JNIEnv*, jobject, jclass, jmethodID, ...) CallNonvirtualByteMethod; 2653 jbyte function(JNIEnv*, jobject, jclass, jmethodID, va_list) CallNonvirtualByteMethodV; 2654 jbyte function(JNIEnv*, jobject, jclass, jmethodID, jvalue*) CallNonvirtualByteMethodA; 2655 jchar function(JNIEnv*, jobject, jclass, jmethodID, ...) CallNonvirtualCharMethod; 2656 jchar function(JNIEnv*, jobject, jclass, jmethodID, va_list) CallNonvirtualCharMethodV; 2657 jchar function(JNIEnv*, jobject, jclass, jmethodID, jvalue*) CallNonvirtualCharMethodA; 2658 jshort function(JNIEnv*, jobject, jclass, jmethodID, ...) CallNonvirtualShortMethod; 2659 jshort function(JNIEnv*, jobject, jclass, jmethodID, va_list) CallNonvirtualShortMethodV; 2660 jshort function(JNIEnv*, jobject, jclass, jmethodID, jvalue*) CallNonvirtualShortMethodA; 2661 jint function(JNIEnv*, jobject, jclass, jmethodID, ...) CallNonvirtualIntMethod; 2662 jint function(JNIEnv*, jobject, jclass, jmethodID, va_list) CallNonvirtualIntMethodV; 2663 jint function(JNIEnv*, jobject, jclass, jmethodID, jvalue*) CallNonvirtualIntMethodA; 2664 jlong function(JNIEnv*, jobject, jclass, jmethodID, ...) CallNonvirtualLongMethod; 2665 jlong function(JNIEnv*, jobject, jclass, jmethodID, va_list) CallNonvirtualLongMethodV; 2666 jlong function(JNIEnv*, jobject, jclass, jmethodID, jvalue*) CallNonvirtualLongMethodA; 2667 jfloat function(JNIEnv*, jobject, jclass, jmethodID, ...) CallNonvirtualFloatMethod; 2668 jfloat function(JNIEnv*, jobject, jclass, jmethodID, va_list) CallNonvirtualFloatMethodV; 2669 jfloat function(JNIEnv*, jobject, jclass, jmethodID, jvalue*) CallNonvirtualFloatMethodA; 2670 jdouble function(JNIEnv*, jobject, jclass, jmethodID, ...) CallNonvirtualDoubleMethod; 2671 jdouble function(JNIEnv*, jobject, jclass, jmethodID, va_list) CallNonvirtualDoubleMethodV; 2672 jdouble function(JNIEnv*, jobject, jclass, jmethodID, jvalue*) CallNonvirtualDoubleMethodA; 2673 void function(JNIEnv*, jobject, jclass, jmethodID, ...) CallNonvirtualVoidMethod; 2674 void function(JNIEnv*, jobject, jclass, jmethodID, va_list) CallNonvirtualVoidMethodV; 2675 void function(JNIEnv*, jobject, jclass, jmethodID, jvalue*) CallNonvirtualVoidMethodA; 2676 jfieldID function(JNIEnv*, jclass, const(char)*, const(char)*) GetFieldID; 2677 jobject function(JNIEnv*, jobject, jfieldID) GetObjectField; 2678 jboolean function(JNIEnv*, jobject, jfieldID) GetBooleanField; 2679 jbyte function(JNIEnv*, jobject, jfieldID) GetByteField; 2680 jchar function(JNIEnv*, jobject, jfieldID) GetCharField; 2681 jshort function(JNIEnv*, jobject, jfieldID) GetShortField; 2682 jint function(JNIEnv*, jobject, jfieldID) GetIntField; 2683 jlong function(JNIEnv*, jobject, jfieldID) GetLongField; 2684 jfloat function(JNIEnv*, jobject, jfieldID) GetFloatField; 2685 jdouble function(JNIEnv*, jobject, jfieldID) GetDoubleField; 2686 void function(JNIEnv*, jobject, jfieldID, jobject) SetObjectField; 2687 void function(JNIEnv*, jobject, jfieldID, jboolean) SetBooleanField; 2688 void function(JNIEnv*, jobject, jfieldID, jbyte) SetByteField; 2689 void function(JNIEnv*, jobject, jfieldID, jchar) SetCharField; 2690 void function(JNIEnv*, jobject, jfieldID, jshort) SetShortField; 2691 void function(JNIEnv*, jobject, jfieldID, jint) SetIntField; 2692 void function(JNIEnv*, jobject, jfieldID, jlong) SetLongField; 2693 void function(JNIEnv*, jobject, jfieldID, jfloat) SetFloatField; 2694 void function(JNIEnv*, jobject, jfieldID, jdouble) SetDoubleField; 2695 jmethodID function(JNIEnv*, jclass, const(char)*, const(char)*) GetStaticMethodID; 2696 jobject function(JNIEnv*, jclass, jmethodID, ...) CallStaticObjectMethod; 2697 jobject function(JNIEnv*, jclass, jmethodID, va_list) CallStaticObjectMethodV; 2698 jobject function(JNIEnv*, jclass, jmethodID, jvalue*) CallStaticObjectMethodA; 2699 jboolean function(JNIEnv*, jclass, jmethodID, ...) CallStaticBooleanMethod; 2700 jboolean function(JNIEnv*, jclass, jmethodID, va_list) CallStaticBooleanMethodV; 2701 jboolean function(JNIEnv*, jclass, jmethodID, jvalue*) CallStaticBooleanMethodA; 2702 jbyte function(JNIEnv*, jclass, jmethodID, ...) CallStaticByteMethod; 2703 jbyte function(JNIEnv*, jclass, jmethodID, va_list) CallStaticByteMethodV; 2704 jbyte function(JNIEnv*, jclass, jmethodID, jvalue*) CallStaticByteMethodA; 2705 jchar function(JNIEnv*, jclass, jmethodID, ...) CallStaticCharMethod; 2706 jchar function(JNIEnv*, jclass, jmethodID, va_list) CallStaticCharMethodV; 2707 jchar function(JNIEnv*, jclass, jmethodID, jvalue*) CallStaticCharMethodA; 2708 jshort function(JNIEnv*, jclass, jmethodID, ...) CallStaticShortMethod; 2709 jshort function(JNIEnv*, jclass, jmethodID, va_list) CallStaticShortMethodV; 2710 jshort function(JNIEnv*, jclass, jmethodID, jvalue*) CallStaticShortMethodA; 2711 jint function(JNIEnv*, jclass, jmethodID, ...) CallStaticIntMethod; 2712 jint function(JNIEnv*, jclass, jmethodID, va_list) CallStaticIntMethodV; 2713 jint function(JNIEnv*, jclass, jmethodID, jvalue*) CallStaticIntMethodA; 2714 jlong function(JNIEnv*, jclass, jmethodID, ...) CallStaticLongMethod; 2715 jlong function(JNIEnv*, jclass, jmethodID, va_list) CallStaticLongMethodV; 2716 jlong function(JNIEnv*, jclass, jmethodID, jvalue*) CallStaticLongMethodA; 2717 jfloat function(JNIEnv*, jclass, jmethodID, ...) CallStaticFloatMethod; 2718 jfloat function(JNIEnv*, jclass, jmethodID, va_list) CallStaticFloatMethodV; 2719 jfloat function(JNIEnv*, jclass, jmethodID, jvalue*) CallStaticFloatMethodA; 2720 jdouble function(JNIEnv*, jclass, jmethodID, ...) CallStaticDoubleMethod; 2721 jdouble function(JNIEnv*, jclass, jmethodID, va_list) CallStaticDoubleMethodV; 2722 jdouble function(JNIEnv*, jclass, jmethodID, jvalue*) CallStaticDoubleMethodA; 2723 void function(JNIEnv*, jclass, jmethodID, ...) CallStaticVoidMethod; 2724 void function(JNIEnv*, jclass, jmethodID, va_list) CallStaticVoidMethodV; 2725 void function(JNIEnv*, jclass, jmethodID, jvalue*) CallStaticVoidMethodA; 2726 jfieldID function(JNIEnv*, jclass, const(char)*, const(char)*) GetStaticFieldID; 2727 jobject function(JNIEnv*, jclass, jfieldID) GetStaticObjectField; 2728 jboolean function(JNIEnv*, jclass, jfieldID) GetStaticBooleanField; 2729 jbyte function(JNIEnv*, jclass, jfieldID) GetStaticByteField; 2730 jchar function(JNIEnv*, jclass, jfieldID) GetStaticCharField; 2731 jshort function(JNIEnv*, jclass, jfieldID) GetStaticShortField; 2732 jint function(JNIEnv*, jclass, jfieldID) GetStaticIntField; 2733 jlong function(JNIEnv*, jclass, jfieldID) GetStaticLongField; 2734 jfloat function(JNIEnv*, jclass, jfieldID) GetStaticFloatField; 2735 jdouble function(JNIEnv*, jclass, jfieldID) GetStaticDoubleField; 2736 void function(JNIEnv*, jclass, jfieldID, jobject) SetStaticObjectField; 2737 void function(JNIEnv*, jclass, jfieldID, jboolean) SetStaticBooleanField; 2738 void function(JNIEnv*, jclass, jfieldID, jbyte) SetStaticByteField; 2739 void function(JNIEnv*, jclass, jfieldID, jchar) SetStaticCharField; 2740 void function(JNIEnv*, jclass, jfieldID, jshort) SetStaticShortField; 2741 void function(JNIEnv*, jclass, jfieldID, jint) SetStaticIntField; 2742 void function(JNIEnv*, jclass, jfieldID, jlong) SetStaticLongField; 2743 void function(JNIEnv*, jclass, jfieldID, jfloat) SetStaticFloatField; 2744 void function(JNIEnv*, jclass, jfieldID, jdouble) SetStaticDoubleField; 2745 jstring function(JNIEnv*, const(jchar)*, jsize) NewString; 2746 jsize function(JNIEnv*, jstring) GetStringLength; 2747 const(jchar)* function(JNIEnv*, jstring, jboolean*) GetStringChars; 2748 void function(JNIEnv*, jstring, const(jchar)*) ReleaseStringChars; 2749 jstring function(JNIEnv*, const(char)*) NewStringUTF; 2750 jsize function(JNIEnv*, jstring) GetStringUTFLength; 2751 const(char)* function(JNIEnv*, jstring, jboolean*) GetStringUTFChars; 2752 void function(JNIEnv*, jstring, const(char)*) ReleaseStringUTFChars; 2753 jsize function(JNIEnv*, jarray) GetArrayLength; 2754 jobjectArray function(JNIEnv*, jsize, jclass, jobject) NewObjectArray; 2755 jobject function(JNIEnv*, jobjectArray, jsize) GetObjectArrayElement; 2756 void function(JNIEnv*, jobjectArray, jsize, jobject) SetObjectArrayElement; 2757 jbooleanArray function(JNIEnv*, jsize) NewBooleanArray; 2758 jbyteArray function(JNIEnv*, jsize) NewByteArray; 2759 jcharArray function(JNIEnv*, jsize) NewCharArray; 2760 jshortArray function(JNIEnv*, jsize) NewShortArray; 2761 jintArray function(JNIEnv*, jsize) NewIntArray; 2762 jlongArray function(JNIEnv*, jsize) NewLongArray; 2763 jfloatArray function(JNIEnv*, jsize) NewFloatArray; 2764 jdoubleArray function(JNIEnv*, jsize) NewDoubleArray; 2765 jboolean* function(JNIEnv*, jbooleanArray, jboolean*) GetBooleanArrayElements; 2766 jbyte* function(JNIEnv*, jbyteArray, jboolean*) GetByteArrayElements; 2767 jchar* function(JNIEnv*, jcharArray, jboolean*) GetCharArrayElements; 2768 jshort* function(JNIEnv*, jshortArray, jboolean*) GetShortArrayElements; 2769 jint* function(JNIEnv*, jintArray, jboolean*) GetIntArrayElements; 2770 jlong* function(JNIEnv*, jlongArray, jboolean*) GetLongArrayElements; 2771 jfloat* function(JNIEnv*, jfloatArray, jboolean*) GetFloatArrayElements; 2772 jdouble* function(JNIEnv*, jdoubleArray, jboolean*) GetDoubleArrayElements; 2773 void function(JNIEnv*, jbooleanArray, jboolean*, jint) ReleaseBooleanArrayElements; 2774 void function(JNIEnv*, jbyteArray, jbyte*, jint) ReleaseByteArrayElements; 2775 void function(JNIEnv*, jcharArray, jchar*, jint) ReleaseCharArrayElements; 2776 void function(JNIEnv*, jshortArray, jshort*, jint) ReleaseShortArrayElements; 2777 void function(JNIEnv*, jintArray, jint*, jint) ReleaseIntArrayElements; 2778 void function(JNIEnv*, jlongArray, jlong*, jint) ReleaseLongArrayElements; 2779 void function(JNIEnv*, jfloatArray, jfloat*, jint) ReleaseFloatArrayElements; 2780 void function(JNIEnv*, jdoubleArray, jdouble*, jint) ReleaseDoubleArrayElements; 2781 void function(JNIEnv*, jbooleanArray, jsize, jsize, jboolean*) GetBooleanArrayRegion; 2782 void function(JNIEnv*, jbyteArray, jsize, jsize, jbyte*) GetByteArrayRegion; 2783 void function(JNIEnv*, jcharArray, jsize, jsize, jchar*) GetCharArrayRegion; 2784 void function(JNIEnv*, jshortArray, jsize, jsize, jshort*) GetShortArrayRegion; 2785 void function(JNIEnv*, jintArray, jsize, jsize, jint*) GetIntArrayRegion; 2786 void function(JNIEnv*, jlongArray, jsize, jsize, jlong*) GetLongArrayRegion; 2787 void function(JNIEnv*, jfloatArray, jsize, jsize, jfloat*) GetFloatArrayRegion; 2788 void function(JNIEnv*, jdoubleArray, jsize, jsize, jdouble*) GetDoubleArrayRegion; 2789 void function(JNIEnv*, jbooleanArray, jsize, jsize, const(jboolean)*) SetBooleanArrayRegion; 2790 void function(JNIEnv*, jbyteArray, jsize, jsize, const(jbyte)*) SetByteArrayRegion; 2791 void function(JNIEnv*, jcharArray, jsize, jsize, const(jchar)*) SetCharArrayRegion; 2792 void function(JNIEnv*, jshortArray, jsize, jsize, const(jshort)*) SetShortArrayRegion; 2793 void function(JNIEnv*, jintArray, jsize, jsize, const(jint)*) SetIntArrayRegion; 2794 void function(JNIEnv*, jlongArray, jsize, jsize, const(jlong)*) SetLongArrayRegion; 2795 void function(JNIEnv*, jfloatArray, jsize, jsize, const(jfloat)*) SetFloatArrayRegion; 2796 void function(JNIEnv*, jdoubleArray, jsize, jsize, const(jdouble)*) SetDoubleArrayRegion; 2797 jint function(JNIEnv*, jclass, const(JNINativeMethod)*, jint) RegisterNatives; 2798 jint function(JNIEnv*, jclass) UnregisterNatives; 2799 jint function(JNIEnv*, jobject) MonitorEnter; 2800 jint function(JNIEnv*, jobject) MonitorExit; 2801 jint function(JNIEnv*, JavaVM**) GetJavaVM; 2802 void function(JNIEnv*, jstring, jsize, jsize, jchar*) GetStringRegion; 2803 void function(JNIEnv*, jstring, jsize, jsize, char*) GetStringUTFRegion; 2804 void* function(JNIEnv*, jarray, jboolean*) GetPrimitiveArrayCritical; 2805 void function(JNIEnv*, jarray, void*, jint) ReleasePrimitiveArrayCritical; 2806 const(jchar)* function(JNIEnv*, jstring, jboolean*) GetStringCritical; 2807 void function(JNIEnv*, jstring, const(jchar)*) ReleaseStringCritical; 2808 jweak function(JNIEnv*, jobject) NewWeakGlobalRef; 2809 void function(JNIEnv*, jweak) DeleteWeakGlobalRef; 2810 jboolean function(JNIEnv*) ExceptionCheck; 2811 jobject function(JNIEnv*, void*, jlong) NewDirectByteBuffer; 2812 void* function(JNIEnv*, jobject) GetDirectBufferAddress; 2813 jlong function(JNIEnv*, jobject) GetDirectBufferCapacity; 2814 jobjectRefType function(JNIEnv*, jobject) GetObjectRefType; // since version 6 2815 jobject GetModule(JNIEnv *env, jclass clazz); // since version 9 2816 } 2817 2818 struct _JNIEnv 2819 { 2820 const(JNINativeInterface)* functions; 2821 } 2822 2823 struct JNIInvokeInterface 2824 { 2825 void* reserved0; 2826 void* reserved1; 2827 void* reserved2; 2828 jint function(JavaVM*) DestroyJavaVM; 2829 jint function(JavaVM*, JNIEnv**, void*) AttachCurrentThread; 2830 jint function(JavaVM*) DetachCurrentThread; 2831 jint function(JavaVM*, void**, jint) GetEnv; 2832 jint function(JavaVM*, JNIEnv**, void*) AttachCurrentThreadAsDaemon; 2833 } 2834 2835 struct _JavaVM 2836 { 2837 const(JNIInvokeInterface)* functions; 2838 } 2839 2840 struct JavaVMAttachArgs 2841 { 2842 jint version_; 2843 const(char)* name; 2844 jobject group; 2845 } 2846 2847 struct JavaVMOption 2848 { 2849 const(char)* optionString; 2850 void* extraInfo; 2851 } 2852 2853 struct JavaVMInitArgs 2854 { 2855 jint version_; 2856 jint nOptions; 2857 JavaVMOption* options; 2858 jboolean ignoreUnrecognized; 2859 } 2860 2861 jint JNI_GetDefaultJavaVMInitArgs(void *args); 2862 jint JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args); 2863 jint JNI_GetCreatedJavaVMs(JavaVM **, jsize, jsize *); 2864 2865 struct _jfieldID; 2866 struct _jmethodID; 2867 2868 union jvalue 2869 { 2870 jboolean z; 2871 jbyte b; 2872 jchar c; 2873 jshort s; 2874 jint i; 2875 jlong j; 2876 jfloat f; 2877 jdouble d; 2878 jobject l; 2879 } 2880 2881 /* 2882 Copyright 2019-2021, Adam D. Ruppe. 2883 Boost license. or whatever. 2884 Most work done in December 2019. 2885 */