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