mirror of https://github.com/adamdruppe/arsd.git
lots of cool stuff
This commit is contained in:
parent
643527c897
commit
35d58a4c0d
411
jni.d
411
jni.d
|
@ -120,15 +120,85 @@
|
||||||
+/
|
+/
|
||||||
module arsd.jni;
|
module arsd.jni;
|
||||||
|
|
||||||
// FIXME: java.lang.CharSequence is the interface for String. We should support that just as well.
|
// FIXME: if user defines an interface with the appropriate RAII return values,
|
||||||
|
// it should let them do that for more efficiency
|
||||||
|
// e.g. @Import Manual!(int[]) getJavaArray();
|
||||||
|
|
||||||
|
/+
|
||||||
|
final class CharSequence : JavaClass!("java.lang", CharSequence) {
|
||||||
|
@Import string toString(); // this triggers a dmd segfault! whoa. FIXME dmd
|
||||||
|
}
|
||||||
|
+/
|
||||||
|
|
||||||
|
/++
|
||||||
|
This is an interface in Java that the built in String class
|
||||||
|
implements. In D, our string is not an object at all, and
|
||||||
|
overloading an auto-generated method with a user-defined one
|
||||||
|
is a bit awkward and not automatic, so I'm instead cheating
|
||||||
|
here and pretending it is a class we can construct in a D
|
||||||
|
overload.
|
||||||
|
|
||||||
|
This is not a great solution. I'm also considering a UDA
|
||||||
|
on the param to indicate Java actually expects the interface,
|
||||||
|
or even a runtime lookup to disambiguate, but meh for now
|
||||||
|
I'm just sticking with this slightly wasteful overload/construct.
|
||||||
|
+/
|
||||||
|
final class CharSequence : JavaClass!("java.lang", CharSequence) {
|
||||||
|
this(string data) {
|
||||||
|
auto env = activeEnv;
|
||||||
|
assert(env !is null);
|
||||||
|
|
||||||
|
wchar[1024] buffer;
|
||||||
|
const(wchar)[] translated;
|
||||||
|
if(data.length < 1024) {
|
||||||
|
size_t len;
|
||||||
|
foreach(wchar ch; data)
|
||||||
|
buffer[len++] = ch;
|
||||||
|
translated = buffer[0 .. len];
|
||||||
|
} else {
|
||||||
|
import std.conv;
|
||||||
|
translated = to!wstring(data);
|
||||||
|
}
|
||||||
|
// Java copies the buffer so it is perfectly fine to return here now
|
||||||
|
internalJavaHandle_ = (*env).NewString(env, translated.ptr, cast(jsize) translated.length);
|
||||||
|
}
|
||||||
|
this(wstring data) {
|
||||||
|
auto env = activeEnv;
|
||||||
|
assert(env !is null);
|
||||||
|
internalJavaHandle_ = (*env).NewString(env, data.ptr, cast(jsize) data.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/++
|
||||||
|
Can be used as a UDA for methods or classes where the D name
|
||||||
|
and the Java name don't match (for example, if it happens to be
|
||||||
|
a D keyword).
|
||||||
|
|
||||||
|
---
|
||||||
|
@JavaName("version")
|
||||||
|
@Import int version_();
|
||||||
|
---
|
||||||
|
+/
|
||||||
|
struct JavaName {
|
||||||
|
string name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string getJavaName(alias a)() {
|
||||||
|
string name = __traits(identifier, a);
|
||||||
|
static foreach(attr; __traits(getAttributes, a))
|
||||||
|
static if(is(typeof(attr) == JavaName))
|
||||||
|
name = attr.name;
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// semi-FIXME: java.lang.CharSequence is the interface for String. We should support that just as well.
|
||||||
|
// possibly other boxed types too, like Integer.
|
||||||
// FIXME: in general, handle substituting subclasses for interfaces nicely
|
// FIXME: in general, handle substituting subclasses for interfaces nicely
|
||||||
|
|
||||||
// FIXME: solve the globalref/pin issue with the type system
|
// FIXME: solve the globalref/pin issue with the type system
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
// FIXME: in general i didn't handle overloads at all. Using RegisterNatives should make that doable... and I might be able to remove some of the pragma(mangle) that way too. And get more error checking done up-front.
|
|
||||||
|
|
||||||
// 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.
|
// 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.
|
||||||
|
|
||||||
/+
|
/+
|
||||||
|
@ -153,30 +223,8 @@ module arsd.jni;
|
||||||
//pragma(crt_constructor) // fyi
|
//pragma(crt_constructor) // fyi
|
||||||
//pragma(crt_destructor)
|
//pragma(crt_destructor)
|
||||||
|
|
||||||
// FIXME: put this in a mixin instead of assuming it is needed/wanted?
|
extern(System)
|
||||||
|
export jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||||
extern(C)
|
|
||||||
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|
||||||
/+
|
|
||||||
|
|
||||||
JNIEnv* env;
|
|
||||||
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
|
|
||||||
return JNI_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find your class. JNI_OnLoad is called from the correct class loader context for this to work.
|
|
||||||
jclass c = env->FindClass("com/example/app/package/MyClass");
|
|
||||||
if (c == nullptr) return JNI_ERR;
|
|
||||||
|
|
||||||
// Register your class' native methods.
|
|
||||||
static const JNINativeMethod methods[] = {
|
|
||||||
{"nativeFoo", "()V", reinterpret_cast(nativeFoo)},
|
|
||||||
{"nativeBar", "(Ljava/lang/String;I)Z", reinterpret_cast(nativeBar)},
|
|
||||||
};
|
|
||||||
int rc = env->RegisterNatives(c, methods, sizeof(methods)/sizeof(JNINativeMethod));
|
|
||||||
if (rc != JNI_OK) return rc;
|
|
||||||
|
|
||||||
+/
|
|
||||||
try {
|
try {
|
||||||
import core.runtime;
|
import core.runtime;
|
||||||
// note this is OK if it is already initialized
|
// note this is OK if it is already initialized
|
||||||
|
@ -187,10 +235,20 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||||
}
|
}
|
||||||
|
|
||||||
activeJvm = vm;
|
activeJvm = vm;
|
||||||
|
|
||||||
|
JNIEnv* env;
|
||||||
|
if ((*vm).GetEnv(vm, cast(void**) &env, JNI_VERSION_1_6) != JNI_OK) {
|
||||||
|
return JNI_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(init; classInitializers_)
|
||||||
|
if(init(env) != 0)
|
||||||
|
return JNI_ERR;
|
||||||
|
|
||||||
return JNI_VERSION_1_6;
|
return JNI_VERSION_1_6;
|
||||||
}
|
}
|
||||||
extern(C)
|
extern(System)
|
||||||
void JNI_OnUnload(JavaVM* vm, void* reserved) {
|
export void JNI_OnUnload(JavaVM* vm, void* reserved) {
|
||||||
activeJvm = null;
|
activeJvm = null;
|
||||||
import core.runtime;
|
import core.runtime;
|
||||||
try {
|
try {
|
||||||
|
@ -224,17 +282,44 @@ struct ActivateJniEnv {
|
||||||
|
|
||||||
/++
|
/++
|
||||||
Creates a JVM for use when `main` is in D. Keep the returned
|
Creates a JVM for use when `main` is in D. Keep the returned
|
||||||
struct around until you are done with it.
|
struct around until you are done with it. While this struct
|
||||||
|
is live, you can use imported Java classes and methods almost
|
||||||
|
as if they were written in D.
|
||||||
|
|
||||||
If `main` is in Java, this is not necessary and should not be
|
If `main` is in Java, this is not necessary and should not be
|
||||||
used.
|
used.
|
||||||
|
|
||||||
If you use this, you will need to link in a jni shared lib.
|
|
||||||
|
This function will try to load the jvm with dynamic runtime
|
||||||
|
linking. For this to succeed:
|
||||||
|
|
||||||
|
On Windows, make sure the path to `jvm.dll` is in your `PATH`
|
||||||
|
environment variable, or installed system wide. For example:
|
||||||
|
|
||||||
|
$(CONSOLE
|
||||||
|
set PATH=%PATH%;c:\users\me\program\jdk-13.0.1\bin\server
|
||||||
|
)
|
||||||
|
|
||||||
|
On Linux (and I think Mac), set `LD_LIBRARY_PATH` environment
|
||||||
|
variable to include the path to `libjvm.so`.
|
||||||
|
|
||||||
|
$(CONSOLE
|
||||||
|
export LD_LIBRARY_PATH=/home/me/jdk-13.0.1/bin/server
|
||||||
|
)
|
||||||
|
|
||||||
|
Failure to do this will throw an exception along the lines of
|
||||||
|
"no jvm dll" in the message.
|
||||||
+/
|
+/
|
||||||
auto createJvm()() {
|
auto createJvm()() {
|
||||||
struct JVM {
|
version(Windows)
|
||||||
|
import core.sys.windows.windows;
|
||||||
|
else
|
||||||
|
import core.sys.posix.dlfcn;
|
||||||
|
|
||||||
|
static struct JVM {
|
||||||
ActivateJniEnv e;
|
ActivateJniEnv e;
|
||||||
JavaVM* pvm;
|
JavaVM* pvm;
|
||||||
|
void* jvmdll;
|
||||||
|
|
||||||
@disable this(this);
|
@disable this(this);
|
||||||
|
|
||||||
|
@ -242,22 +327,77 @@ auto createJvm()() {
|
||||||
if(pvm)
|
if(pvm)
|
||||||
(*pvm).DestroyJavaVM(pvm);
|
(*pvm).DestroyJavaVM(pvm);
|
||||||
activeJvm = null;
|
activeJvm = null;
|
||||||
|
|
||||||
|
version(Windows) {
|
||||||
|
if(jvmdll) FreeLibrary(jvmdll);
|
||||||
|
} else {
|
||||||
|
if(jvmdll) dlclose(jvmdll);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JavaVM* pvm;
|
JavaVM* pvm;
|
||||||
JNIEnv* env;
|
JNIEnv* env;
|
||||||
auto res = JNI_CreateJavaVM(&pvm, cast(void**) &env, null);
|
|
||||||
|
JavaVMInitArgs vm_args;
|
||||||
|
JavaVMOption[0] options;
|
||||||
|
|
||||||
|
//options[0].optionString = "-Djava.compiler=NONE"; /* disable JIT */
|
||||||
|
//options[1].optionString = "-verbose:jni"; /* print JNI-related messages */
|
||||||
|
//options[1].optionString = `-Djava.class.path=c:\Users\me\program\jni\`; /* user classes */
|
||||||
|
//options[2].optionString = `-Djava.library.path=c:\Users\me\program\jdk-13.0.1\lib\`; /* set native library path */
|
||||||
|
|
||||||
|
vm_args.version_ = JNI_VERSION_1_6;
|
||||||
|
vm_args.options = options.ptr;
|
||||||
|
vm_args.nOptions = cast(int) options.length;
|
||||||
|
vm_args.ignoreUnrecognized = true;
|
||||||
|
|
||||||
|
//import std.process;
|
||||||
|
//environment["PATH"] = environment["PATH"] ~ `;c:\users\me\program\jdk-13.0.1\bin\server`;
|
||||||
|
|
||||||
|
version(Windows)
|
||||||
|
auto jvmdll = LoadLibraryW("jvm.dll"w.ptr);
|
||||||
|
else
|
||||||
|
auto jvmdll = dlopen("libjvm.so");
|
||||||
|
|
||||||
|
if(jvmdll is null)
|
||||||
|
throw new Exception("no jvm dll");
|
||||||
|
|
||||||
|
version(Windows)
|
||||||
|
auto fn = cast(typeof(&JNI_CreateJavaVM)) GetProcAddress(jvmdll, "JNI_CreateJavaVM");
|
||||||
|
else
|
||||||
|
auto fn = cast(typeof(&JNI_CreateJavaVM)) dlsym(jvmdll, "JNI_CreateJavaVM");
|
||||||
|
|
||||||
|
if(fn is null)
|
||||||
|
throw new Exception("no fun");
|
||||||
|
|
||||||
|
auto res = fn(&pvm, cast(void**) &env, &vm_args);//, args);
|
||||||
if(res != JNI_OK)
|
if(res != JNI_OK)
|
||||||
throw new Exception("create jvm failed"); // FIXME: throw res);
|
throw new Exception("create jvm failed"); // FIXME: throw res);
|
||||||
|
|
||||||
activeJvm = pvm;
|
activeJvm = pvm;
|
||||||
|
|
||||||
return JVM(ActivateJniEnv(env), pvm);
|
return JVM(ActivateJniEnv(env), pvm, jvmdll);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
version(Windows)
|
||||||
|
private extern(Windows) bool SetDllDirectoryW(wstring);
|
||||||
|
|
||||||
|
|
||||||
|
@JavaName("Throwable")
|
||||||
|
final class JavaThrowable : JavaClass!("java.lang", JavaThrowable) {
|
||||||
|
@Import string getMessage();
|
||||||
|
@Import StackTraceElement[] getStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
final class StackTraceElement : JavaClass!("java.lang", StackTraceElement) {
|
||||||
|
@Import this(string declaringClass, string methodName, string fileName, int lineNumber);
|
||||||
|
@Import string getClassName();
|
||||||
|
@Import string getFileName();
|
||||||
|
@Import int getLineNumber();
|
||||||
|
@Import string getMethodName();
|
||||||
|
@Import bool isNativeMethod();
|
||||||
|
}
|
||||||
|
|
||||||
private void exceptionCheck(JNIEnv* env) {
|
private void exceptionCheck(JNIEnv* env) {
|
||||||
if((*env).ExceptionCheck(env)) {
|
if((*env).ExceptionCheck(env)) {
|
||||||
|
@ -266,6 +406,7 @@ private void exceptionCheck(JNIEnv* env) {
|
||||||
// do I need to free thrown?
|
// do I need to free thrown?
|
||||||
(*env).ExceptionClear(env);
|
(*env).ExceptionClear(env);
|
||||||
|
|
||||||
|
// FIXME
|
||||||
throw new Exception("Java threw");
|
throw new Exception("Java threw");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -274,6 +415,31 @@ private enum ImportImplementationString = q{
|
||||||
static if(is(typeof(return) == void)) {
|
static if(is(typeof(return) == void)) {
|
||||||
(*env).CallSTATICVoidMethod(env, jobj, _jmethodID, DDataToJni(env, args).args);
|
(*env).CallSTATICVoidMethod(env, jobj, _jmethodID, DDataToJni(env, args).args);
|
||||||
exceptionCheck(env);
|
exceptionCheck(env);
|
||||||
|
} else static if(is(typeof(return) == string) || is(typeof(return) == wstring)) {
|
||||||
|
// I can't just use JavaParamsToD here btw because of lifetime worries.
|
||||||
|
// maybe i should fix it down there though because there is a lot of duplication
|
||||||
|
|
||||||
|
auto jret = (*env).CallSTATICObjectMethod(env, jobj, _jmethodID, DDataToJni(env, args).args);
|
||||||
|
exceptionCheck(env);
|
||||||
|
|
||||||
|
typeof(return) ret;
|
||||||
|
|
||||||
|
auto len = (*env).GetStringLength(env, jret);
|
||||||
|
auto ptr = (*env).GetStringChars(env, jret, null);
|
||||||
|
static if(is(T == wstring)) {
|
||||||
|
if(ptr !is null) {
|
||||||
|
ret = ptr[0 .. len].idup;
|
||||||
|
(*env).ReleaseStringChars(env, jret, ptr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
import std.conv;
|
||||||
|
if(ptr !is null) {
|
||||||
|
ret = to!string(ptr[0 .. len]);
|
||||||
|
(*env).ReleaseStringChars(env, jret, ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
} else static if(is(typeof(return) == int)) {
|
} else static if(is(typeof(return) == int)) {
|
||||||
auto ret = (*env).CallSTATICIntMethod(env, jobj, _jmethodID, DDataToJni(env, args).args);
|
auto ret = (*env).CallSTATICIntMethod(env, jobj, _jmethodID, DDataToJni(env, args).args);
|
||||||
exceptionCheck(env);
|
exceptionCheck(env);
|
||||||
|
@ -302,13 +468,57 @@ private enum ImportImplementationString = q{
|
||||||
auto ret = (*env).CallSTATICCharMethod(env, jobj, _jmethodID, DDataToJni(env, args).args);
|
auto ret = (*env).CallSTATICCharMethod(env, jobj, _jmethodID, DDataToJni(env, args).args);
|
||||||
exceptionCheck(env);
|
exceptionCheck(env);
|
||||||
return ret;
|
return ret;
|
||||||
|
} else static if(is(typeof(return) == E[], E)) {
|
||||||
|
// Java arrays are represented as objects
|
||||||
|
auto jarr = (*env).CallSTATICObjectMethod(env, jobj, _jmethodID, DDataToJni(env, args).args);
|
||||||
|
exceptionCheck(env);
|
||||||
|
|
||||||
|
auto len = (*env).GetArrayLength(env, jarr);
|
||||||
|
static if(is(E == int)) {
|
||||||
|
auto eles = (*env).GetIntArrayElements(env, jarr, null);
|
||||||
|
auto res = eles[0 .. len];
|
||||||
|
(*env).ReleaseIntArrayElements(env, jarr, eles, 0);
|
||||||
|
} else static if(is(E == bool)) {
|
||||||
|
auto eles = (*env).GetBooleanArrayElements(env, jarr, null);
|
||||||
|
auto res = eles[0 .. len];
|
||||||
|
(*env).ReleaseBooleanArrayElements(env, jarr, eles, 0);
|
||||||
|
} else static if(is(E == long)) {
|
||||||
|
auto eles = (*env).GetLongArrayElements(env, jarr, null);
|
||||||
|
auto res = eles[0 .. len];
|
||||||
|
(*env).ReleaseLongArrayElements(env, jarr, eles, 0);
|
||||||
|
} else static if(is(E == short)) {
|
||||||
|
auto eles = (*env).GetShortArrayElements(env, jarr, null);
|
||||||
|
auto res = eles[0 .. len];
|
||||||
|
(*env).ReleaseShortArrayElements(env, jarr, eles, 0);
|
||||||
|
} else static if(is(E == wchar)) {
|
||||||
|
auto eles = (*env).GetCharArrayElements(env, jarr, null);
|
||||||
|
auto res = eles[0 .. len];
|
||||||
|
(*env).ReleaseCharArrayElements(env, jarr, eles, 0);
|
||||||
|
} else static if(is(E == float)) {
|
||||||
|
auto eles = (*env).GetFloatArrayElements(env, jarr, null);
|
||||||
|
auto res = eles[0 .. len];
|
||||||
|
(*env).ReleaseFloatArrayElements(env, jarr, eles, 0);
|
||||||
|
} else static if(is(E == double)) {
|
||||||
|
auto eles = (*env).GetDoubleArrayElements(env, jarr, null);
|
||||||
|
auto res = eles[0 .. len];
|
||||||
|
(*env).ReleaseDoubleArrayElements(env, jarr, eles, 0);
|
||||||
|
} else static if(is(E == byte)) {
|
||||||
|
auto eles = (*env).GetByteArrayElements(env, jarr, null);
|
||||||
|
auto res = eles[0 .. len];
|
||||||
|
(*env).ReleaseByteArrayElements(env, jarr, eles, 0);
|
||||||
|
} else static if(is(E : IJavaObject)) {
|
||||||
|
// FIXME: implement this
|
||||||
|
typeof(return) res = null;
|
||||||
|
} 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.
|
||||||
|
|
||||||
|
return res;
|
||||||
} else {
|
} else {
|
||||||
static assert(0, "Unsupported return type for JNI " ~ typeof(return).stringof);
|
static assert(0, "Unsupported return type for JNI " ~ typeof(return).stringof);
|
||||||
//return DDataToJni(env, __traits(getMember, dobj, __traits(identifier, method))(JavaParamsToD!(Parameters!method)(env, args).args));
|
//return DDataToJni(env, __traits(getMember, dobj, __traits(identifier, method))(JavaParamsToD!(Parameters!method)(env, args).args));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private mixin template JavaImportImpl(T, alias method) {
|
private mixin template JavaImportImpl(T, alias method, size_t overloadIndex) {
|
||||||
import std.traits;
|
import std.traits;
|
||||||
|
|
||||||
private static jmethodID _jmethodID;
|
private static jmethodID _jmethodID;
|
||||||
|
@ -362,7 +572,7 @@ private mixin template JavaImportImpl(T, alias method) {
|
||||||
jc = internalJavaClassHandle_;
|
jc = internalJavaClassHandle_;
|
||||||
}
|
}
|
||||||
_jmethodID = (*env).GetStaticMethodID(env, jc,
|
_jmethodID = (*env).GetStaticMethodID(env, jc,
|
||||||
__traits(identifier, method).ptr,
|
getJavaName!method.ptr,
|
||||||
// java method string is (args)ret
|
// java method string is (args)ret
|
||||||
("(" ~ DTypesToJniString!(typeof(args)) ~ ")" ~ DTypesToJniString!(typeof(return)) ~ "\0").ptr
|
("(" ~ DTypesToJniString!(typeof(args)) ~ ")" ~ DTypesToJniString!(typeof(return)) ~ "\0").ptr
|
||||||
);
|
);
|
||||||
|
@ -387,7 +597,7 @@ private mixin template JavaImportImpl(T, alias method) {
|
||||||
if(!_jmethodID) {
|
if(!_jmethodID) {
|
||||||
auto jc = (*env).GetObjectClass(env, jobj);
|
auto jc = (*env).GetObjectClass(env, jobj);
|
||||||
_jmethodID = (*env).GetMethodID(env, jc,
|
_jmethodID = (*env).GetMethodID(env, jc,
|
||||||
__traits(identifier, method).ptr,
|
getJavaName!method.ptr,
|
||||||
// java method string is (args)ret
|
// java method string is (args)ret
|
||||||
("(" ~ DTypesToJniString!(typeof(args)) ~ ")" ~ DTypesToJniString!(typeof(return)) ~ "\0").ptr
|
("(" ~ DTypesToJniString!(typeof(args)) ~ ")" ~ DTypesToJniString!(typeof(return)) ~ "\0").ptr
|
||||||
);
|
);
|
||||||
|
@ -403,62 +613,49 @@ private mixin template JavaImportImpl(T, alias method) {
|
||||||
|
|
||||||
private template DTypesToJniString(Types...) {
|
private template DTypesToJniString(Types...) {
|
||||||
static if(Types.length == 0)
|
static if(Types.length == 0)
|
||||||
string DTypesToJniString = "";
|
enum string DTypesToJniString = "";
|
||||||
else static if(Types.length == 1) {
|
else static if(Types.length == 1) {
|
||||||
alias T = Types[0];
|
alias T = Types[0];
|
||||||
|
|
||||||
static if(is(T == void))
|
static if(is(T == void))
|
||||||
string DTypesToJniString = "V";
|
enum string DTypesToJniString = "V";
|
||||||
else static if(is(T == string))
|
else static if(is(T == string)) {
|
||||||
string DTypesToJniString = "Ljava/lang/String;";
|
enum string DTypesToJniString = "Ljava/lang/String;";
|
||||||
else static if(is(T == wstring))
|
} else static if(is(T == wstring))
|
||||||
string DTypesToJniString = "Ljava/lang/String;";
|
enum string DTypesToJniString = "Ljava/lang/String;";
|
||||||
else static if(is(T == int))
|
else static if(is(T == int))
|
||||||
string DTypesToJniString = "I";
|
enum string DTypesToJniString = "I";
|
||||||
else static if(is(T == bool))
|
else static if(is(T == bool))
|
||||||
string DTypesToJniString = "Z";
|
enum string DTypesToJniString = "Z";
|
||||||
else static if(is(T == byte))
|
else static if(is(T == byte))
|
||||||
string DTypesToJniString = "B";
|
enum string DTypesToJniString = "B";
|
||||||
else static if(is(T == wchar))
|
else static if(is(T == wchar))
|
||||||
string DTypesToJniString = "C";
|
enum string DTypesToJniString = "C";
|
||||||
else static if(is(T == short))
|
else static if(is(T == short))
|
||||||
string DTypesToJniString = "S";
|
enum string DTypesToJniString = "S";
|
||||||
else static if(is(T == long))
|
else static if(is(T == long))
|
||||||
string DTypesToJniString = "J";
|
enum string DTypesToJniString = "J";
|
||||||
else static if(is(T == float))
|
else static if(is(T == float))
|
||||||
string DTypesToJniString = "F";
|
enum string DTypesToJniString = "F";
|
||||||
else static if(is(T == double))
|
else static if(is(T == double))
|
||||||
string DTypesToJniString = "D";
|
enum string DTypesToJniString = "D";
|
||||||
else static if(is(T == size_t))
|
else static if(is(T == size_t))
|
||||||
string DTypesToJniString = "I"; // possible FIXME...
|
enum string DTypesToJniString = "I"; // possible FIXME...
|
||||||
else static if(is(T == IJavaObject))
|
else static if(is(T == IJavaObject))
|
||||||
string DTypesToJniString = "LObject;"; // FIXME?
|
enum string DTypesToJniString = "LObject;"; // FIXME?
|
||||||
else static if(is(T : IJavaObject)) // child of this but a concrete type
|
else static if(is(T : IJavaObject)) // child of this but a concrete type
|
||||||
string DTypesToJniString = T._javaParameterString;
|
enum string DTypesToJniString = T._javaParameterString;
|
||||||
/+ // FIXME they are just "[" ~ element type string
|
else static if(is(T == E[], E))
|
||||||
else static if(is(T == IJavaObject[]))
|
enum string DTypesToJniString = "[" ~ DTypesToJniString!E;
|
||||||
string DTypesToJniString = jobjectArray;
|
|
||||||
else static if(is(T == bool[]))
|
|
||||||
string DTypesToJniString = jbooleanArray;
|
|
||||||
else static if(is(T == byte[]))
|
|
||||||
string DTypesToJniString = jbyteArray;
|
|
||||||
else static if(is(T == wchar[]))
|
|
||||||
string DTypesToJniString = jcharArray;
|
|
||||||
else static if(is(T == short[]))
|
|
||||||
string DTypesToJniString = jshortArray;
|
|
||||||
else static if(is(T == int[]))
|
|
||||||
string DTypesToJniString = jintArray;
|
|
||||||
else static if(is(T == long[]))
|
|
||||||
string DTypesToJniString = jlongArray;
|
|
||||||
else static if(is(T == float[]))
|
|
||||||
string DTypesToJniString = jfloatArray;
|
|
||||||
else static if(is(T == double[]))
|
|
||||||
string DTypesToJniString = jdoubleArray;
|
|
||||||
+/
|
|
||||||
else static assert(0, "Unsupported type for JNI call " ~ T.stringof);
|
else static assert(0, "Unsupported type for JNI call " ~ T.stringof);
|
||||||
} else {
|
} else {
|
||||||
import std.typecons;
|
private string helper() {
|
||||||
string DTypesToJni = DTypesToJni!(Types[0]) ~ DTypesToJni(Types[1 .. $]);
|
string s;
|
||||||
|
foreach(Type; Types)
|
||||||
|
s ~= DTypesToJniString!Type;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
enum string DTypesToJniString = helper;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -515,8 +712,10 @@ private template DTypesToJni(Types...) {
|
||||||
alias DTypesToJni = jdoubleArray;
|
alias DTypesToJni = jdoubleArray;
|
||||||
else static assert(0, "Unsupported type for JNI " ~ T.stringof);
|
else static assert(0, "Unsupported type for JNI " ~ T.stringof);
|
||||||
} else {
|
} else {
|
||||||
import std.typecons;
|
import std.meta;
|
||||||
alias DTypesToJni = AliasSeq!(DTypesToJni!(Types[0]), DTypesToJni(Types[1 .. $]));
|
// FIXME: write about this later if you forget the ! on the final DTypesToJni, dmd
|
||||||
|
// says "error: recursive template expansion". woof.
|
||||||
|
alias DTypesToJni = AliasSeq!(DTypesToJni!(Types[0]), DTypesToJni!(Types[1 .. $]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -562,7 +761,7 @@ auto DDatumToJni(T)(JNIEnv* env, T data) {
|
||||||
else static if(is(T == float)) return data;
|
else static if(is(T == float)) return data;
|
||||||
else static if(is(T == double)) return data;
|
else static if(is(T == double)) return data;
|
||||||
else static if(is(T == size_t)) return cast(int) data;
|
else static if(is(T == size_t)) return cast(int) data;
|
||||||
else static if(is(T : IJavaObject)) return data.getJavaHandle();
|
else static if(is(T : IJavaObject)) return data is null ? null : data.getJavaHandle();
|
||||||
else static assert(0, "Unsupported type " ~ T.stringof);
|
else static assert(0, "Unsupported type " ~ T.stringof);
|
||||||
/* // FIXME: finish these.
|
/* // FIXME: finish these.
|
||||||
else static if(is(T == IJavaObject[]))
|
else static if(is(T == IJavaObject[]))
|
||||||
|
@ -675,22 +874,24 @@ void jniRethrow(JNIEnv* env, Throwable t) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private mixin template JavaExportImpl(T, alias method) {
|
private mixin template JavaExportImpl(T, alias method, size_t overloadIndex) {
|
||||||
import std.traits;
|
import std.traits;
|
||||||
import std.string;
|
import std.string;
|
||||||
|
|
||||||
static if(__traits(identifier, method) == "__ctor")
|
static if(__traits(identifier, method) == "__ctor")
|
||||||
static assert(0, "Cannot export D constructors");
|
static assert(0, "Cannot export D constructors");
|
||||||
|
|
||||||
|
/+
|
||||||
static private string JniMangle() {
|
static private string JniMangle() {
|
||||||
// this actually breaks with -betterC though so does a lot more so meh.
|
// this actually breaks with -betterC though so does a lot more so meh.
|
||||||
static if(is(T : JavaClass!(JP, P), string JP, P))
|
static if(is(T : JavaClass!(JP, P), string JP, P))
|
||||||
return "Java_" ~replace(JP, ".", "_") ~ (JP.length ? "_" : "") ~ P.stringof ~ "_" ~ __traits(identifier, method);
|
return "Java_" ~replace(JP, ".", "_") ~ (JP.length ? "_" : "") ~ P.stringof ~ "_" ~ __traits(identifier, method);
|
||||||
else static assert(0);
|
else static assert(0);
|
||||||
}
|
}
|
||||||
|
+/
|
||||||
|
|
||||||
extern(C)
|
extern(System)
|
||||||
pragma(mangle, JniMangle())
|
//pragma(mangle, JniMangle())
|
||||||
// I need it in the DLL, but want it to be not accessible from outside... alas.
|
// I need it in the DLL, but want it to be not accessible from outside... alas.
|
||||||
export /*private*/ static DTypesToJni!(ReturnType!method) privateJniImplementation(JNIEnv* env, jobject obj, DTypesToJni!(Parameters!method) args) {
|
export /*private*/ static DTypesToJni!(ReturnType!method) privateJniImplementation(JNIEnv* env, jobject obj, DTypesToJni!(Parameters!method) args) {
|
||||||
// set it up in the thread for future calls
|
// set it up in the thread for future calls
|
||||||
|
@ -716,19 +917,28 @@ private mixin template JavaExportImpl(T, alias method) {
|
||||||
|
|
||||||
static if(is(typeof(return) == void)) {
|
static if(is(typeof(return) == void)) {
|
||||||
try {
|
try {
|
||||||
__traits(getMember, dobj, __traits(identifier, method))(JavaParamsToD!(Parameters!method)(env, args).args);
|
__traits(getOverloads, dobj, __traits(identifier, method))[overloadIndex](JavaParamsToD!(Parameters!method)(env, args).args);
|
||||||
} catch(Throwable t) {
|
} catch(Throwable t) {
|
||||||
jniRethrow(env, t);
|
jniRethrow(env, t);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
return DDatumToJni(env, __traits(getMember, dobj, __traits(identifier, method))(JavaParamsToD!(Parameters!method)(env, args).args));
|
return DDatumToJni(env, __traits(getOverloads, dobj, __traits(identifier, method))[overloadIndex](JavaParamsToD!(Parameters!method)(env, args).args));
|
||||||
} catch(Throwable t) {
|
} catch(Throwable t) {
|
||||||
jniRethrow(env, t);
|
jniRethrow(env, t);
|
||||||
return typeof(return).init; // still required to return...
|
return typeof(return).init; // still required to return...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
shared static this() {
|
||||||
|
nativeMethodsData_ ~= JNINativeMethod(
|
||||||
|
getJavaName!method.ptr,
|
||||||
|
("(" ~ DTypesToJniString!(Parameters!method) ~ ")" ~ DTypesToJniString!(ReturnType!method) ~ "\0").ptr,
|
||||||
|
&privateJniImplementation
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/++
|
/++
|
||||||
|
@ -776,27 +986,48 @@ class JavaClass(string javaPackage, CRTP) : IJavaObject {
|
||||||
static assert("JavaClasses can only be constructed by Java. Try making a constructor in Java, then make an @Import this(args); here.");
|
static assert("JavaClasses can only be constructed by Java. Try making a constructor in Java, then make an @Import this(args); here.");
|
||||||
|
|
||||||
// implementations
|
// implementations
|
||||||
static foreach(attr; __traits(getAttributes, __traits(getMember, CRTP, memberName))) {
|
static foreach(oi, overload; __traits(getOverloads, CRTP, memberName))
|
||||||
|
static foreach(attr; __traits(getAttributes, overload)) {
|
||||||
static if(is(attr == Import))
|
static if(is(attr == Import))
|
||||||
mixin JavaImportImpl!(CRTP, __traits(getMember, CRTP, memberName));
|
mixin JavaImportImpl!(CRTP, overload, oi);
|
||||||
else static if(is(attr == Export))
|
else static if(is(attr == Export))
|
||||||
mixin JavaExportImpl!(CRTP, __traits(getMember, CRTP, memberName));
|
mixin JavaExportImpl!(CRTP, overload, oi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected jobject internalJavaHandle_;
|
protected jobject internalJavaHandle_;
|
||||||
protected jobject getJavaHandle() { return internalJavaHandle_; }
|
protected jobject getJavaHandle() { return internalJavaHandle_; }
|
||||||
|
|
||||||
|
__gshared static protected /*immutable*/ JNINativeMethod[] nativeMethodsData_;
|
||||||
protected static jclass internalJavaClassHandle_;
|
protected static jclass internalJavaClassHandle_;
|
||||||
|
protected static int initializeInJvm_(JNIEnv* env) {
|
||||||
|
|
||||||
|
import core.stdc.stdio;
|
||||||
|
auto internalJavaClassHandle_ = (*env).FindClass(env, (_javaParameterString[1 .. $-1] ~ "\0").ptr);
|
||||||
|
if(!internalJavaClassHandle_) {
|
||||||
|
fprintf(stderr, ("Cannot find Java class for " ~ CRTP.stringof));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((*env).RegisterNatives(env, internalJavaClassHandle_, nativeMethodsData_.ptr, cast(int) nativeMethodsData_.length)) {
|
||||||
|
fprintf(stderr, ("RegisterNatives failed for " ~ CRTP.stringof));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
shared static this() {
|
||||||
|
classInitializers_ ~= &initializeInJvm_;
|
||||||
|
}
|
||||||
|
|
||||||
static import std.string;
|
static import std.string;
|
||||||
static if(javaPackage.length)
|
static if(javaPackage.length)
|
||||||
public static immutable string _javaParameterString = "L" ~ std.string.replace(javaPackage, ".", "/") ~ "/" ~ CRTP.stringof ~ ";";
|
public static immutable string _javaParameterString = "L" ~ std.string.replace(javaPackage, ".", "/") ~ "/" ~ getJavaName!CRTP ~ ";";
|
||||||
else
|
else
|
||||||
public static immutable string _javaParameterString = "L" ~ CRTP.stringof ~ ";";
|
public static immutable string _javaParameterString = "L" ~ getJavaName!CRTP ~ ";";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__gshared /* immutable */ int function(JNIEnv* env)[] classInitializers_;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -820,7 +1051,7 @@ class JavaClass(string javaPackage, CRTP) : IJavaObject {
|
||||||
import core.stdc.stdarg;
|
import core.stdc.stdarg;
|
||||||
|
|
||||||
//version (Android):
|
//version (Android):
|
||||||
extern (C):
|
extern (System):
|
||||||
@system:
|
@system:
|
||||||
nothrow:
|
nothrow:
|
||||||
@nogc:
|
@nogc:
|
||||||
|
|
Loading…
Reference in New Issue