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