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