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