experimental partial jni support
This commit is contained in:
Родитель
5a6db03cf2
Коммит
90b9f2199f
|
@ -0,0 +1,133 @@
|
|||
|
||||
// Emscripten shims for JVM support
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "emjvm.h"
|
||||
|
||||
// invoke interface
|
||||
|
||||
jint DestroyJavaVM(JavaVM*) { exit(55); return NULL; }
|
||||
jint AttachCurrentThread(JavaVM*, JNIEnv**, void*) { exit(66); return NULL; }
|
||||
jint DetachCurrentThread(JavaVM*) { exit(77); return NULL; }
|
||||
jint GetEnv(JavaVM*, void** env, jint); // forward def
|
||||
jint AttachCurrentThreadAsDaemon(JavaVM*, JNIEnv**, void*) { exit(88); return NULL; }
|
||||
|
||||
// env - some of these are externs that are implemented in JS
|
||||
|
||||
jobject EMJVM_NewGlobalRef(JNIEnv*, jobject obj) {
|
||||
return obj; // XXX no global refcounting, we just keep global singletons alive etc.
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
extern jstring emjvm_newString(const jchar *chars, jsize len);
|
||||
}
|
||||
jstring EMJVM_NewString(JNIEnv*, const jchar* unicodeChars, jsize len) {
|
||||
return emjvm_newString(unicodeChars, len);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
extern jclass emjvm_getObjectClass(JNIEnv*, jobject obj);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
extern jmethodID emjvm_getMethodID(jclass, const char*, const char*);
|
||||
}
|
||||
extern jmethodID EMJVM_GetMethodID(JNIEnv*, jclass clazz, const char* methodName, const char* sig) {
|
||||
return emjvm_getMethodID(clazz, methodName, sig);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
extern jobject emjvm_callObjectMethod(JNIEnv*, jobject, jmethodID, va_list);
|
||||
|
||||
extern void emjvm_deleteLocalRef(JNIEnv*, jobject);
|
||||
|
||||
jsize emjvm_getArrayLength(JNIEnv*, jarray);
|
||||
|
||||
void emjvm_getByteArrayRegion(JNIEnv*, jbyteArray, jsize, jsize, jbyte*);
|
||||
|
||||
jclass emjvm_findClass(JNIEnv*, const char*);
|
||||
|
||||
jmethodID emjvm_getStaticMethodID(JNIEnv*, jclass, const char*, const char*);
|
||||
|
||||
jobject emjvm_callStaticObjectMethod(JNIEnv*, jclass, jmethodID, va_list);
|
||||
|
||||
jboolean emjvm_callBooleanMethod(JNIEnv*, jobject, jmethodID, va_list);
|
||||
jboolean emjvm_callStaticBooleanMethod(JNIEnv*, jclass, jmethodID, va_list);
|
||||
|
||||
void emjvm_callVoidMethod(JNIEnv*, jobject, jmethodID, va_list);
|
||||
|
||||
jint emjvm_callIntMethod(JNIEnv*, jobject, jmethodID, va_list);
|
||||
|
||||
const char* emjvm_getStringUTFChars(JNIEnv*, jstring, jboolean*);
|
||||
jsize emjvm_getStringUTFLength(JNIEnv*, jstring);
|
||||
void emjvm_releaseStringUTFChars(JNIEnv*, jstring, const char*);
|
||||
}
|
||||
|
||||
// JVM
|
||||
|
||||
struct EmJVM {
|
||||
JavaVM jvm;
|
||||
JNIInvokeInterface jvmFunctions;
|
||||
|
||||
JNIEnv env;
|
||||
JNINativeInterface envFunctions;
|
||||
|
||||
EmJVM() {
|
||||
// jvm
|
||||
jvm.functions = &jvmFunctions;
|
||||
|
||||
jvmFunctions.DestroyJavaVM = DestroyJavaVM;
|
||||
jvmFunctions.AttachCurrentThread = AttachCurrentThread;
|
||||
jvmFunctions.DetachCurrentThread = DetachCurrentThread;
|
||||
jvmFunctions.GetEnv = GetEnv;
|
||||
jvmFunctions.AttachCurrentThreadAsDaemon = AttachCurrentThreadAsDaemon;
|
||||
|
||||
// env
|
||||
memset(&envFunctions, 0, sizeof(envFunctions));
|
||||
|
||||
env.functions = &envFunctions;
|
||||
|
||||
envFunctions.NewGlobalRef = EMJVM_NewGlobalRef;
|
||||
envFunctions.NewString = EMJVM_NewString;
|
||||
envFunctions.GetObjectClass = emjvm_getObjectClass;
|
||||
envFunctions.GetMethodID = EMJVM_GetMethodID;
|
||||
envFunctions.CallObjectMethodV = emjvm_callObjectMethod;
|
||||
envFunctions.DeleteLocalRef = emjvm_deleteLocalRef;
|
||||
envFunctions.GetArrayLength = emjvm_getArrayLength;
|
||||
envFunctions.GetByteArrayRegion = emjvm_getByteArrayRegion;
|
||||
envFunctions.FindClass = emjvm_findClass;
|
||||
envFunctions.GetStaticMethodID = emjvm_getStaticMethodID;
|
||||
envFunctions.CallStaticObjectMethodV = emjvm_callStaticObjectMethod;
|
||||
envFunctions.CallBooleanMethodV = emjvm_callBooleanMethod;
|
||||
envFunctions.CallStaticBooleanMethodV = emjvm_callStaticBooleanMethod;
|
||||
envFunctions.CallVoidMethodV = emjvm_callVoidMethod;
|
||||
envFunctions.CallIntMethodV = emjvm_callIntMethod;
|
||||
envFunctions.GetStringUTFChars = emjvm_getStringUTFChars;
|
||||
envFunctions.GetStringUTFLength = emjvm_getStringUTFLength;
|
||||
envFunctions.ReleaseStringUTFChars = emjvm_releaseStringUTFChars;
|
||||
}
|
||||
};
|
||||
|
||||
EmJVM emJVM;
|
||||
|
||||
// implement forward defs
|
||||
|
||||
jint GetEnv(JavaVM*, void** env, jint) {
|
||||
*env = &emJVM.env;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// external access from JS
|
||||
|
||||
extern "C" {
|
||||
|
||||
JavaVM* emscripten_get_jvm() { return &emJVM.jvm; }
|
||||
|
||||
JNIEnv* emscripten_get_jni_env() { return &emJVM.env; }
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
#include "jni.h"
|
||||
|
||||
extern "C" {
|
||||
extern JavaVM* emscripten_get_jvm();
|
||||
extern JNIEnv* emscripten_get_jni_env();
|
||||
}
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
|
||||
var EmJVM = {
|
||||
debug: false,
|
||||
|
||||
nextId: 0,
|
||||
objects: {},
|
||||
classNames: {}, // class name => singleton object
|
||||
|
||||
addObject: function(o) {
|
||||
var ret = EmJVM.nextId++;
|
||||
EmJVM.objects[ret] = o;
|
||||
o.id = ret;
|
||||
o.refs = 1;
|
||||
o.nextMethodId = 0;
|
||||
// XXX Module.print('add object ' + JSON.stringify(o).substr(0, 80) + (ret > 5285 ? new Error().stack : ''));
|
||||
return ret;
|
||||
},
|
||||
|
||||
addSingletonObject: function(o) {
|
||||
EmJVM.classNames[o.name] = o;
|
||||
return EmJVM.addObject(o);
|
||||
},
|
||||
|
||||
createString: function(data) {
|
||||
return EmJVM.addObject({ name: 'string', value: data });
|
||||
},
|
||||
|
||||
createByteArray: function(data) {
|
||||
return EmJVM.addObject({ name: 'byteArray', value: data });
|
||||
},
|
||||
};
|
||||
|
||||
function widecharToString(ptr, len) {
|
||||
var nullTerminated = typeof(len) == "undefined";
|
||||
var ret = "";
|
||||
var i = 0;
|
||||
var t;
|
||||
while (1) {
|
||||
t = getValue(ptr + 2 * i, 'i16');
|
||||
if (nullTerminated && t == 0) break;
|
||||
if (t != 0) {
|
||||
ret += String.fromCharCode(t);
|
||||
}
|
||||
++i;
|
||||
if (!nullTerminated && i == len) break;
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
function _emjvm_newString(chars, len) {
|
||||
return EmJVM.createString(widecharToString(chars, len));
|
||||
}
|
||||
|
||||
function _emjvm_getStringUTFChars(jniEnv, string, isCopy) {
|
||||
var obj = EmJVM.objects[string];
|
||||
assert(obj.name == 'string');
|
||||
if (isCopy) setValue(isCopy, 'i8', 1);
|
||||
var buffer = _malloc(obj.value.length+1);
|
||||
writeStringToMemory(obj.value, buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
function _emjvm_getStringUTFLength(jniEnv, string) {
|
||||
var obj = EmJVM.objects[string];
|
||||
if (obj.value) {
|
||||
return obj.value.length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function _emjvm_releaseStringUTFChars(jniEnv, string, utf) {
|
||||
}
|
||||
|
||||
function _emjvm_getObjectClass(env, jobject) {
|
||||
if (EmJVM.debug) {
|
||||
console.log('EMJVM_GetObjectClass+AddLocalRef: ' + [jobject]);
|
||||
}
|
||||
var obj = EmJVM.objects[jobject];
|
||||
obj.refs++;
|
||||
return jobject;
|
||||
}
|
||||
|
||||
function _emjvm_getMethodID(jclass, name, sig) {
|
||||
if (EmJVM.debug) {
|
||||
console.log('EMJVM_GetMethodID: ' + [jclass, Pointer_stringify(name), Pointer_stringify(sig)]);
|
||||
console.log('EMJVM_GetMethodID: ' + [EmJVM.objects[jclass].name]);
|
||||
}
|
||||
// assumes class <--> object, just called on singletons
|
||||
name = Pointer_stringify(name);
|
||||
var obj = EmJVM.objects[jclass];
|
||||
if (!obj[name]) {
|
||||
throw 'missing implementation for ' + obj.name + '::' + name + ' : ' + new Error().stack;
|
||||
}
|
||||
if (!obj[name + '__methodId']) {
|
||||
var methodId = obj.nextMethodId++;
|
||||
obj[name + '__methodId'] = methodId;
|
||||
obj['method__' + methodId] = obj[name];
|
||||
obj['methodName__' + methodId] = name;
|
||||
}
|
||||
return obj[name + '__methodId'];
|
||||
}
|
||||
|
||||
function _emjvm_getStaticMethodID(jniEnv, jclass, name, sig) {
|
||||
// Pretend this to be the same as looking up a non-static method
|
||||
return _emjvm_getMethodID(jclass, name, sig);
|
||||
}
|
||||
|
||||
function _emjvm_callObjectMethod(jniEnv, jobject, methodId, varargs) {
|
||||
if (EmJVM.debug) {
|
||||
console.log('EMJVM_CallObjectMethod: ' + [jobject, EmJVM.objects[jobject].name, methodId, EmJVM.objects[jobject]['methodName__' + methodId]]);
|
||||
}
|
||||
return EmJVM.objects[jobject]['method__' + methodId](varargs);
|
||||
}
|
||||
|
||||
function _emjvm_callStaticObjectMethod(jniEnv, jclass, methodId, varargs) {
|
||||
// Pretend this to be the same as calling a non-static method
|
||||
return _emjvm_callObjectMethod(jniEnv, jclass, methodId, varargs);
|
||||
}
|
||||
|
||||
function _emjvm_callStaticBooleanMethod(jniEnv, jclass, methodId, varargs) {
|
||||
// Only differs in return type
|
||||
return _emjvm_callStaticObjectMethod(jniEnv, jclass, methodId, varargs);
|
||||
}
|
||||
|
||||
function _emjvm_callBooleanMethod(jniEnv, jobject, methodId, varargs) {
|
||||
// Pretend this to be the same as calling a non-static method
|
||||
return _emjvm_callStaticBooleanMethod(jniEnv, jobject, methodId, varargs);
|
||||
}
|
||||
|
||||
function _emjvm_callVoidMethod(jniEnv, jobject, methodId, varargs) {
|
||||
_emjvm_callObjectMethod(jniEnv, jobject, methodId, varargs);
|
||||
}
|
||||
|
||||
function _emjvm_callIntMethod(jniEnv, jobject, methodId, varargs) {
|
||||
return _emjvm_callObjectMethod(jniEnv, jobject, methodId, varargs);
|
||||
}
|
||||
|
||||
function _emjvm_deleteLocalRef(jniEnv, jobject) {
|
||||
if (EmJVM.debug) {
|
||||
console.log('EMJVM_DeleteLocalRef: ' + [jobject]);
|
||||
}
|
||||
var obj = EmJVM.objects[jobject];
|
||||
obj.refs--;
|
||||
if (obj.refs == 0) {
|
||||
if (EmJVM.debug) {
|
||||
console.log('EMJVM_DeleteLocalRef: remove ' + obj.name);
|
||||
}
|
||||
delete EmJVM.objects[jobject];
|
||||
}
|
||||
}
|
||||
|
||||
function _emjvm_getArrayLength(jniEnv, jobject) {
|
||||
var obj = EmJVM.objects[jobject];
|
||||
assert(obj.name == 'byteArray');
|
||||
return obj.value.length;
|
||||
}
|
||||
|
||||
function _emjvm_getByteArrayRegion(jniEnv, jobject, start, len, buf) {
|
||||
var obj = EmJVM.objects[jobject];
|
||||
assert(obj.name == 'byteArray');
|
||||
assert(obj.value); // we set this to null below and assume we are never called again
|
||||
if (EmJVM.debug) {
|
||||
console.log('emjvm_getByteArrayRegion: ' + [jobject, obj.value.length, start, len, buf]);
|
||||
}
|
||||
assert(start + len <= obj.value.length);
|
||||
assert(len == obj.value.length); // we assume users read it all, and we can now copy it all with set() and then free it
|
||||
HEAPU8.set(obj.value, buf);
|
||||
obj.value = null; // XXX assume byte arrays are one-shot
|
||||
}
|
||||
|
||||
function _emjvm_findClass(env, name) {
|
||||
name = Pointer_stringify(name);
|
||||
if (EmJVM.debug) {
|
||||
console.log('emjvm_findClass: ' + [name]);
|
||||
}
|
||||
var obj = EmJVM.classNames[name];
|
||||
assert(obj);
|
||||
return obj.id;
|
||||
}
|
||||
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче