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