on develop help sign in
Use byte arrays across the c/java boundary

Instead of strings

commitebeb2e771878c8427878b775b6813e06e4675b10 authorRobert J. Macomber <robertm@dilithium> dateSun 21 Jan 2018, 05:37 (9 years ago) parent0afc206
4 files changed+56−32
reflex-dom/cbits/MainWidget.c+25−12 reflex-dom/cbits/include/MainWidget.h+4−2 reflex-dom/java/org/reflexfrp/reflexdom/MainWidget.java+9−6 reflex-dom/src-android/Reflex/Dom/Android/MainWidget.hsc+18−12
reflex-dom/cbits/MainWidget.c+25−12
@@ -1,5 +1,6 @@
11#include <jni.h>
22#include <assert.h>
3+#include <stdlib.h>
34#include <android/log.h>
45#include <HaskellActivity.h>
56#include "MainWidget.h"
@@ -29,7 +30,7 @@ jobject Reflex_Dom_Android_MainWidget_start(jobject activity, const char *url, c
2930 return (*env)->NewGlobalRef(env, result);
3031}
3132
32−void Reflex_Dom_Android_MainWidget_runJS(jobject jsExecutor, const char* js) {
33+void Reflex_Dom_Android_MainWidget_runJS(jobject jsExecutor, const char* js, size_t js_len) {
3334 JNIEnv *env;
3435 jint attachResult = (*HaskellActivity_jvm)->AttachCurrentThread(HaskellActivity_jvm, &env, NULL);
3536 assert (attachResult == JNI_OK);
@@ -39,9 +40,10 @@ void Reflex_Dom_Android_MainWidget_runJS(jobject jsExecutor, const char* js) {
3940 //TODO: Don't search for this method every time
4041 jclass cls = (*env)->GetObjectClass(env, jsExecutor);
4142 assert(cls);
42− jmethodID evaluateJavascript = (*env)->GetMethodID(env, cls, "evaluateJavascript", "(Ljava/lang/String;)V");
43+ jmethodID evaluateJavascript = (*env)->GetMethodID(env, cls, "evaluateJavascript", "([B)V");
4344 assert(evaluateJavascript);
44− jstring js_str = (*env)->NewStringUTF(env, js);
45+ jbyteArray js_str = (*env)->NewByteArray(env, js_len);
46+ (*env)->SetByteArrayRegion(env, js_str, 0, js_len, (const jbyte*)js);
4547 (*env)->CallVoidMethod(env, jsExecutor, evaluateJavascript, js_str, 0);
4648 if((*env)->ExceptionOccurred(env)) {
4749 __android_log_write(ANDROID_LOG_DEBUG, "MainWidget", "runJS exception");
@@ -57,20 +59,31 @@ JNIEXPORT void JNICALL Java_org_reflexfrp_reflexdom_MainWidget_00024JSaddleCallb
5759 return;
5860}
5961
60−JNIEXPORT void JNICALL Java_org_reflexfrp_reflexdom_MainWidget_00024JSaddleCallbacks_processMessage (JNIEnv *env, jobject thisObj, jlong callbacksLong, jstring msg) {
62+JNIEXPORT void JNICALL Java_org_reflexfrp_reflexdom_MainWidget_00024JSaddleCallbacks_processMessage (JNIEnv *env, jobject thisObj, jlong callbacksLong, jbyteArray msg) {
6163 const JSaddleCallbacks *callbacks = (const JSaddleCallbacks *)callbacksLong;
62− const char *msg_str = (*env)->GetStringUTFChars(env, msg, NULL);
63− (*(callbacks->jsaddleResult))(msg_str);
64− (*env)->ReleaseStringUTFChars(env, msg, msg_str);
64+
65+ jbyte *msg_str = (*env)->GetByteArrayElements(env, msg, NULL);
66+ jsize msg_str_len = (*env)->GetArrayLength(env, msg);
67+ (*(callbacks->jsaddleResult))(msg_str, msg_str_len);
68+ (*env)->ReleaseByteArrayElements(env, msg, msg_str, JNI_ABORT);
6569 return;
6670}
6771
68−JNIEXPORT jstring JNICALL Java_org_reflexfrp_reflexdom_MainWidget_00024JSaddleCallbacks_processSyncMessage (JNIEnv *env, jobject thisObj, jlong callbacksLong, jstring msg) {
72+JNIEXPORT jbyteArray JNICALL Java_org_reflexfrp_reflexdom_MainWidget_00024JSaddleCallbacks_processSyncMessage (JNIEnv *env, jobject thisObj, jlong callbacksLong, jbyteArray msg) {
6973 const JSaddleCallbacks *callbacks = (const JSaddleCallbacks *)callbacksLong;
70− const char *msg_str = (*env)->GetStringUTFChars(env, msg, NULL);
71− char *next_str = (*(callbacks->jsaddleSyncResult))(msg_str);
72− jstring next_jstr = (*env)->NewStringUTF(env,next_str);
74+
75+ char const *next_str;
76+ size_t next_str_len;
77+
78+ jbyte *msg_str = (*env)->GetByteArrayElements(env, msg, NULL);
79+ jsize msg_str_len = (*env)->GetArrayLength(env, msg);
80+ (*(callbacks->jsaddleSyncResult))(msg_str, msg_str_len, &next_str, &next_str_len);
81+ (*env)->ReleaseByteArrayElements(env, msg, msg_str, JNI_ABORT);
82+
83+ jbyteArray next_jstr = (*env)->NewByteArray(env, next_str_len);
84+ (*env)->SetByteArrayRegion(env, next_jstr, 0, next_str_len, next_str);
85+
7386 free(next_str);
74− (*env)->ReleaseStringUTFChars(env, msg, msg_str);
87+
7588 return next_jstr;
7689}
reflex-dom/cbits/include/MainWidget.h+4−2
@@ -1,10 +1,12 @@
11#ifndef MAINWIDGET_H_INCLUDED
22#define MAINWIDGET_H_INCLUDED
33
4+#include <stddef.h>
5+
46typedef struct JSaddleCallbacks {
57 void (* jsaddleStart) ();
6− void (* jsaddleResult) (const char *);
7− char * (* jsaddleSyncResult) (const char *);
8+ void (* jsaddleResult) (const char *, size_t);
9+ void (* jsaddleSyncResult) (const char *, size_t, const char**, size_t*);
810 char * jsaddleJsData;
911} JSaddleCallbacks;
1012
reflex-dom/java/org/reflexfrp/reflexdom/MainWidget.java+9−6
@@ -11,6 +11,7 @@ import android.webkit.WebSettings;
1111import android.webkit.WebView;
1212import android.webkit.WebViewClient;
1313
14+import java.nio.charset.StandardCharsets;
1415
1516public class MainWidget {
1617 private static Object startMainWidget(Activity a, String url, long jsaddleCallbacks, final String initialJS) {
@@ -44,11 +45,12 @@ public class MainWidget {
4445
4546 final Handler hnd = new Handler();
4647 return new Object() {
47− public final void evaluateJavascript(final String js) {
48+ public final void evaluateJavascript(final byte[] js) {
49+ final String jsStr = new String(js, StandardCharsets.UTF_8);
4850 hnd.post(new Runnable() {
4951 @Override
5052 public void run() {
51− wv.evaluateJavascript(js, null);
53+ wv.evaluateJavascript(jsStr, null);
5254 }
5355 });
5456 }
@@ -58,8 +60,8 @@ public class MainWidget {
5860 private static class JSaddleCallbacks {
5961 private final long callbacks;
6062 private native void startProcessing(long callbacks);
61− private native void processMessage(long callbacks, String msg);
62− private native String processSyncMessage(long callbacks, String msg);
63+ private native void processMessage(long callbacks, byte[] msg);
64+ private native byte[] processSyncMessage(long callbacks, byte[] msg);
6365
6466 public JSaddleCallbacks(long _callbacks) {
6567 callbacks = _callbacks;
@@ -73,13 +75,14 @@ public class MainWidget {
7375
7476 @JavascriptInterface
7577 public boolean postMessage(final String msg) {
76− processMessage(callbacks, msg);
78+ processMessage(callbacks, msg.getBytes(StandardCharsets.UTF_8));
7779 return true;
7880 }
7981
8082 @JavascriptInterface
8183 public String syncMessage(final String msg) {
82− return processSyncMessage(callbacks, msg);
84+ return new String(processSyncMessage(callbacks, msg.getBytes(StandardCharsets.UTF_8)),
85+ StandardCharsets.UTF_8);
8386 }
8487 }
8588}
reflex-dom/src-android/Reflex/Dom/Android/MainWidget.hsc+18−12
@@ -15,6 +15,7 @@ import qualified Data.ByteString.Unsafe as BSU
1515import Data.IORef
1616import Data.Monoid
1717import Foreign.C.String
18+import Foreign.C.Types
1819import Foreign.Marshal.Utils
1920import Foreign.Marshal.Array
2021import Foreign.Ptr
@@ -32,8 +33,8 @@ startMainWidget a url jsm = do
3233 executorRef <- newIORef $ error "startMainWidget: executor not created yet"
3334 let go batch = do
3435 executor <- readIORef executorRef
35− BS.useAsCString (LBS.toStrict $ "runJSaddleBatch(" <> encode batch <> ");") $ \cstr -> do
36− runJS executor cstr
36+ BSU.unsafeUseAsCStringLen (LBS.toStrict $ "runJSaddleBatch(" <> encode batch <> ");") $ \(cstr, len) -> do
37+ runJS executor cstr (fromIntegral len)
3738 (processResult, processSyncResult, start) <- runJavaScript go jsm
3839 callbacks <- new <=< jsaddleCallbacksToPtrs $ JSaddleCallbacks
3940 { _jsaddleCallbacks_jsaddleStart = void $ forkIO start
@@ -62,7 +63,7 @@ newtype JSExecutor = JSExecutor { unJSExecutor :: Ptr JSExecutor }
6263
6364foreign import ccall safe "Reflex_Dom_Android_MainWidget_start" startMainWidget_ :: HaskellActivity -> CString -> Ptr JSaddleCallbacksPtrs -> IO JSExecutor
6465
65−foreign import ccall safe "Reflex_Dom_Android_MainWidget_runJS" runJS :: JSExecutor -> CString -> IO ()
66+foreign import ccall safe "Reflex_Dom_Android_MainWidget_runJS" runJS :: JSExecutor -> CString -> CSize -> IO ()
6667
6768data JSaddleCallbacks = JSaddleCallbacks
6869 { _jsaddleCallbacks_jsaddleStart :: IO ()
@@ -73,28 +74,33 @@ data JSaddleCallbacks = JSaddleCallbacks
7374
7475data JSaddleCallbacksPtrs = JSaddleCallbacksPtrs
7576 { _jsaddleCallbacksPtrs_jsaddleStart :: !(FunPtr (IO ()))
76− , _jsaddleCallbacksPtrs_jsaddleResult :: !(FunPtr (CString -> IO ()))
77− , _jsaddleCallbacksPtrs_jsaddleSyncResult :: !(FunPtr (CString -> IO CString))
77+ , _jsaddleCallbacksPtrs_jsaddleResult :: !(FunPtr (CString -> CSize -> IO ()))
78+ , _jsaddleCallbacksPtrs_jsaddleSyncResult :: !(FunPtr (CString -> CSize -> Ptr CString -> Ptr CSize -> IO ()))
7879 , _jsaddleCallbacksPtrs_jsaddleJsData :: !CString
7980 }
8081
8182foreign import ccall "wrapper" wrapIO :: IO () -> IO (FunPtr (IO ()))
82−foreign import ccall "wrapper" wrapCStringIO :: (CString -> IO ()) -> IO (FunPtr (CString -> IO ()))
83−foreign import ccall "wrapper" wrapCStringIOCString :: (CString -> IO CString) -> IO (FunPtr (CString -> IO CString))
83+foreign import ccall "wrapper" wrapCStringCSizeIO :: (CString -> CSize -> IO ()) -> IO (FunPtr (CString -> CSize -> IO ()))
84+foreign import ccall "wrapper" wrapCStringCSizeCStringCSizeIO :: (CString -> CSize -> Ptr CString -> Ptr CSize -> IO ()) -> IO (FunPtr (CString -> CSize -> Ptr CString -> Ptr CSize -> IO ()))
8485
85−newCStringFromByteString :: ByteString -> IO CString
86+newCStringFromByteString :: ByteString -> IO (CString, CSize)
8687newCStringFromByteString bs = BSU.unsafeUseAsCStringLen bs $ \(src, len) -> do
8788 dest <- mallocArray0 len
8889 copyArray dest src len
8990 poke (advancePtr dest len) 0
90− return dest
91+ return (dest, fromIntegral len)
9192
9293jsaddleCallbacksToPtrs :: JSaddleCallbacks -> IO JSaddleCallbacksPtrs
9394jsaddleCallbacksToPtrs jc = JSaddleCallbacksPtrs
9495 <$> wrapIO (_jsaddleCallbacks_jsaddleStart jc)
95− <*> wrapCStringIO (\cstr -> _jsaddleCallbacks_jsaddleResult jc =<< BS.packCString cstr)
96− <*> wrapCStringIOCString (\cstr -> newCStringFromByteString =<< _jsaddleCallbacks_jsaddleSyncResult jc =<< BS.packCString cstr)
97− <*> newCStringFromByteString (_jsaddleCallbacks_jsaddleJsData jc)
96+ <*> wrapCStringCSizeIO (\cstr len -> _jsaddleCallbacks_jsaddleResult jc =<< BS.packCStringLen (cstr, fromIntegral len))
97+ <*> wrapCStringCSizeCStringCSizeIO (\cstr len result resultLen -> do
98+ inStr <- BS.packCStringLen (cstr, fromIntegral len)
99+ outStr <- _jsaddleCallbacks_jsaddleSyncResult jc inStr
100+ (outStrBytes, outStrLen) <- newCStringFromByteString outStr
101+ poke result outStrBytes
102+ poke resultLen outStrLen)
103+ <*> (fst <$> newCStringFromByteString (_jsaddleCallbacks_jsaddleJsData jc))
98104
99105instance Storable JSaddleCallbacksPtrs where
100106 sizeOf _ = #{size JSaddleCallbacks}