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 */