summaryrefslogtreecommitdiff
path: root/library/src
diff options
context:
space:
mode:
Diffstat (limited to 'library/src')
-rw-r--r--library/src/androidTest/java/akono/InstrumentedAkonoTests.kt30
-rw-r--r--library/src/androidTest/kotlin/InstrumentedAkonoTests.kt27
-rw-r--r--library/src/main/cpp/CMakeLists.txt1
-rw-r--r--library/src/main/cpp/akono-jni.cpp290
-rw-r--r--library/src/main/java/akono/AkoniJni.kt39
-rw-r--r--library/src/main/java/akono/Library.kt (renamed from library/src/main/kotlin/akono/Library.kt)0
-rw-r--r--library/src/main/kotlin/akono/AkoniJni.kt13
-rw-r--r--library/src/test/java/akono/LibraryTest.kt (renamed from library/src/test/kotlin/akono/LibraryTest.kt)0
8 files changed, 315 insertions, 85 deletions
diff --git a/library/src/androidTest/java/akono/InstrumentedAkonoTests.kt b/library/src/androidTest/java/akono/InstrumentedAkonoTests.kt
new file mode 100644
index 00000000..b2e8e92a
--- /dev/null
+++ b/library/src/androidTest/java/akono/InstrumentedAkonoTests.kt
@@ -0,0 +1,30 @@
+package akono.test;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.runner.RunWith
+import org.junit.Test
+import androidx.test.filters.SmallTest
+import androidx.test.filters.LargeTest
+import org.junit.Assert.assertTrue
+import org.junit.Assert.assertEquals
+import akono.AkonoJni
+
+// @RunWith is required only if you use a mix of JUnit3 and JUnit4.
+@RunWith(AndroidJUnit4::class)
+@LargeTest
+public class InstrumentedAkonoTestOne {
+ @Test
+ fun myJsTest() {
+ val ajni: AkonoJni = AkonoJni()
+ assertEquals("2", ajni.evalJs("1+1"))
+ assertEquals("36", ajni.evalJs("6*6"))
+ assertEquals("42", ajni.evalJs("(()=>{let x = 42; return x;})()"))
+ //assertEquals(null, ajni.evalJs("throw Error('hello exc')"))
+ //assertEquals(null, ajni.evalJs("undefinedX + undefinedY"))
+ //assertEquals("123", ajni.evalJs("console.log('hello world'); 123;"))
+ //assertEquals("123", ajni.evalJs("require"))
+
+ assertEquals("undefined", ajni.evalJs("const myVal = 42"))
+ assertEquals("43", ajni.evalJs("myVal + 1"))
+ }
+}
diff --git a/library/src/androidTest/kotlin/InstrumentedAkonoTests.kt b/library/src/androidTest/kotlin/InstrumentedAkonoTests.kt
deleted file mode 100644
index 6a59d0ef..00000000
--- a/library/src/androidTest/kotlin/InstrumentedAkonoTests.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-package akono.test;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import org.junit.runner.RunWith
-import org.junit.Test
-import androidx.test.filters.SmallTest
-import androidx.test.filters.LargeTest
-import org.junit.Assert.assertTrue
-import org.junit.Assert.assertEquals
-import akono.AkonoJni
-
-// @RunWith is required only if you use a mix of JUnit3 and JUnit4.
-@RunWith(AndroidJUnit4::class)
-@LargeTest
-public class InstrumentedAkonoTestOne {
- @Test
- fun myAkonoTest() {
- val ajni: AkonoJni = AkonoJni()
- assertEquals("foo", ajni.stringFromJNI())
- }
-
- @Test
- fun myJsTest() {
- val ajni: AkonoJni = AkonoJni()
- assertEquals("2", ajni.evalJs("1+1"));
- }
-}
diff --git a/library/src/main/cpp/CMakeLists.txt b/library/src/main/cpp/CMakeLists.txt
index b101f4bf..72259215 100644
--- a/library/src/main/cpp/CMakeLists.txt
+++ b/library/src/main/cpp/CMakeLists.txt
@@ -16,6 +16,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
include_directories(
${deps_dir}/node/src
${deps_dir}/node/deps/v8/include
+ ${deps_dir}/node/deps/uv/include
)
add_library(node SHARED IMPORTED)
diff --git a/library/src/main/cpp/akono-jni.cpp b/library/src/main/cpp/akono-jni.cpp
index 2131e0eb..f355362b 100644
--- a/library/src/main/cpp/akono-jni.cpp
+++ b/library/src/main/cpp/akono-jni.cpp
@@ -2,57 +2,257 @@
#include <jni.h>
#include <libplatform/libplatform.h>
#include <v8.h>
+#define NODE_WANT_INTERNALS 1
+#include <node.h>
+#include <uv.h>
+#include <unistd.h>
+#include <android/log.h>
-/* This is a trivial JNI example where we use a native method
- * to return a new VM String. See the corresponding Java source
- * file located at:
- *
- * hello-jni/app/src/main/java/com/example/hellojni/HelloJni.java
+
+
+static int pfd[2];
+static pthread_t thr;
+static const char *tag = "myapp";
+
+
+static void *thread_func(void*)
+{
+ ssize_t rdsz;
+ char buf[128];
+ while((rdsz = read(pfd[0], buf, sizeof buf - 1)) > 0) {
+ if(buf[rdsz - 1] == '\n') --rdsz;
+ buf[rdsz] = 0; /* add null-terminator */
+ __android_log_write(ANDROID_LOG_DEBUG, tag, buf);
+ }
+ return 0;
+}
+
+static void mylog(const char *msg)
+{
+ __android_log_write(ANDROID_LOG_DEBUG, tag, msg);
+}
+
+int start_logger(const char *app_name)
+{
+ tag = app_name;
+
+ /* make stdout line-buffered and stderr unbuffered */
+ setvbuf(stdout, 0, _IOLBF, 0);
+ setvbuf(stderr, 0, _IONBF, 0);
+
+ /* create the pipe and redirect stdout and stderr */
+ pipe(pfd);
+ dup2(pfd[1], 1);
+ dup2(pfd[1], 2);
+
+ /* spawn the logging thread */
+ if(pthread_create(&thr, 0, thread_func, 0) == -1)
+ return -1;
+ pthread_detach(thr);
+ return 0;
+}
+
+
+
+/**
+ * Helper class to manage conversion from a JNI string to a C string.
*/
-extern "C" JNIEXPORT jstring JNICALL
-Java_akono_AkonoJni_stringFromJNI(JNIEnv* env, jobject thiz)
+class JStringValue {
+private:
+ jstring m_jstr;
+ const char *m_cstr;
+ JNIEnv* m_env;
+public:
+ JStringValue(JNIEnv* env, jstring s) : m_env(env), m_jstr(s) {
+ m_cstr = env->GetStringUTFChars(s, NULL);
+ }
+ ~JStringValue() {
+ m_env->ReleaseStringUTFChars(m_jstr, m_cstr);
+ }
+ const char *operator*() {
+ return m_cstr;
+ }
+};
+
+
+/**
+ * Slightly more sane wrapper around node::Init
+ */
+static void InitNode(std::vector<const char *> argv) {
+ int ret_exec_argc = 0;
+ int ret_argc = argv.size();
+ const char **ret_exec_argv = nullptr;
+
+ node::Init(&ret_argc, &argv[0], &ret_exec_argc, &ret_exec_argv);
+}
+
+
+class NativeAkonoInstance {
+private:
+ static bool logInitialized;
+ static bool v8Initialized;
+ //static std::unique_ptr<v8::Platform> platform;
+ static node::MultiIsolatePlatform *platform;
+public:
+ v8::Isolate *isolate;
+ node::Environment *environment;
+ v8::Persistent<v8::Context> globalContext;
+
+ NativeAkonoInstance() : globalContext() {
+
+ if (!logInitialized) {
+ start_logger("myapp");
+ logInitialized = true;
+ }
+
+ if (!v8Initialized) {
+ //platform = v8::platform::NewDefaultPlatform();
+ //v8::V8::InitializePlatform(platform.get());
+ //v8::V8::Initialize();
+
+ // Here, only the arguments used to initialize the global node/v8 platform
+ // are relevant, the others are skipped.
+ InitNode(std::vector<const char *>{"node", "--trace-events-enabled"});
+ platform = node::InitializeV8Platform(10);
+ v8::V8::Initialize();
+
+ v8Initialized = true;
+ }
+
+
+ node::ArrayBufferAllocator *allocator = node::CreateArrayBufferAllocator();
+ this->isolate = node::NewIsolate(allocator, uv_default_loop());
+
+
+ v8::Isolate::Scope isolate_scope(isolate);
+ v8::HandleScope handle_scope(isolate);
+
+ node::IsolateData *isolateData = node::CreateIsolateData(
+ this->isolate,
+ uv_default_loop(),
+ platform,
+ allocator);
+
+
+ globalContext.Reset(isolate, v8::Context::New(isolate));
+
+ // Arguments for node itself
+ std::vector<const char*> nodeArgv{"node", "-e", "console.log('hello world');"};
+ // Arguments for the script run by node
+ std::vector<const char*> nodeExecArgv{};
+
+ mylog("entering global scopt");
+
+ v8::Context::Scope context_scope(globalContext.Get(isolate));
+
+ mylog("creating environment");
+
+ node::Environment *environment = node::CreateEnvironment(
+ isolateData,
+ globalContext.Get(isolate),
+ nodeArgv.size(),
+ &nodeArgv[0],
+ nodeExecArgv.size(),
+ &nodeExecArgv[0]);
+
+ mylog("loading environment");
+
+ node::LoadEnvironment(environment);
+
+ mylog("finished environment");
+
+ //v8::Isolate::CreateParams create_params;
+ //create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
+ //this->isolate = v8::Isolate::New(create_params);
+//
+// node::IsolateData *isolateData = node::CreateIsolateData()
+// node::Environment *environment = node::CreateEnvironment(isolateData);
+ }
+
+ ~NativeAkonoInstance() {
+ //this->isolate->Dispose();
+ }
+
+ jstring evalJs(JNIEnv* env, jstring sourceString) {
+ mylog("begin evalJs");
+
+ JStringValue jsv(env, sourceString);
+
+ v8::Isolate::Scope isolate_scope(isolate);
+
+ // Create a stack-allocated handle scope.
+ v8::HandleScope handle_scope(isolate);
+
+ // Create a new context.
+ //v8::Local<v8::Context> context = v8::Context::New(isolate);
+
+ v8::Local<v8::Context> context = globalContext.Get(isolate);
+
+ // Enter the context for compiling and running the hello world script.
+ v8::Context::Scope context_scope(context);
+
+ {
+ // Create a string containing the JavaScript source code.
+ v8::Local<v8::String> source =
+ v8::String::NewFromUtf8(isolate, *jsv,
+ v8::NewStringType::kNormal)
+ .ToLocalChecked();
+
+ // Compile the source code.
+ v8::Local<v8::Script> script;
+
+ mylog("about to compile script");
+
+ if (!v8::Script::Compile(context, source).ToLocal(&script)) {
+ return nullptr;
+ }
+
+ mylog("about to run script");
+
+ // Run the script to get the result.
+ v8::Local<v8::Value> result;
+ if (!script->Run(context).ToLocal(&result)) {
+ mylog("running script failed");
+ return nullptr;
+ }
+
+ mylog("converting script result value");
+
+ // Convert the result to an UTF8 string and print it.
+ v8::String::Utf8Value utf8(isolate, result);
+
+ mylog("about to return value");
+
+ return env->NewStringUTF(*utf8);
+ }
+ }
+};
+
+
+bool NativeAkonoInstance::v8Initialized = false;
+bool NativeAkonoInstance::logInitialized = false;
+node::MultiIsolatePlatform *NativeAkonoInstance::platform = nullptr;
+
+
+extern "C" JNIEXPORT jobject JNICALL
+Java_akono_AkonoJni_initNative(JNIEnv* env, jobject thiz)
+{
+ NativeAkonoInstance *myInstance = new NativeAkonoInstance();
+ return env->NewDirectByteBuffer(myInstance, 0);
+}
+
+
+extern "C" JNIEXPORT void JNICALL
+Java_akono_AkonoJni_destroyNative(JNIEnv* env, jobject thiz, jobject buf)
{
-#if defined(__arm__)
- #if defined(__ARM_ARCH_7A__)
- #if defined(__ARM_NEON__)
- #if defined(__ARM_PCS_VFP)
- #define ABI "armeabi-v7a/NEON (hard-float)"
- #else
- #define ABI "armeabi-v7a/NEON"
- #endif
- #else
- #if defined(__ARM_PCS_VFP)
- #define ABI "armeabi-v7a (hard-float)"
- #else
- #define ABI "armeabi-v7a"
- #endif
- #endif
- #else
- #define ABI "armeabi"
- #endif
-#elif defined(__i386__)
-#define ABI "x86"
-#elif defined(__x86_64__)
-#define ABI "x86_64"
-#elif defined(__mips64) /* mips64el-* toolchain defines __mips__ too */
-#define ABI "mips64"
-#elif defined(__mips__)
-#define ABI "mips"
-#elif defined(__aarch64__)
-#define ABI "arm64-v8a"
-#else
-#define ABI "unknown"
-#endif
-
- return env->NewStringUTF("Hello from JNI ! Compiled with ABI " ABI ".");
+ NativeAkonoInstance *myInstance = (NativeAkonoInstance *) env->GetDirectBufferAddress(buf);
+ delete myInstance;
}
extern "C" JNIEXPORT jstring JNICALL
-Java_akono_AkonoJni_evalJs(JNIEnv* env, jobject thiz, jstring source)
+Java_akono_AkonoJni_evalJs(JNIEnv* env, jobject thiz, jstring sourceStr, jobject buf)
{
- std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
- v8::V8::InitializePlatform(platform.get());
- v8::V8::Initialize();
- return env->NewStringUTF("Hello World");
+ NativeAkonoInstance *myInstance = (NativeAkonoInstance *) env->GetDirectBufferAddress(buf);
+ return myInstance->evalJs(env, sourceStr);
}
diff --git a/library/src/main/java/akono/AkoniJni.kt b/library/src/main/java/akono/AkoniJni.kt
new file mode 100644
index 00000000..2210a7ef
--- /dev/null
+++ b/library/src/main/java/akono/AkoniJni.kt
@@ -0,0 +1,39 @@
+package akono
+
+import java.nio.ByteBuffer
+
+typealias AkonoNativePointer = ByteBuffer
+
+class AkonoJni {
+ external fun stringFromJNI(): String
+
+ private external fun evalJs(source: String, p: AkonoNativePointer): String
+
+ private external fun destroyNative(b: AkonoNativePointer)
+ private external fun initNative(nodeArgv: Array<out String>): AkonoNativePointer
+
+ private external fun runNodeLoop(b: AkonoNativePointer)
+
+ private external fun postMessageToNode(message: String, b: AkonoNativePointer)
+
+ private external fun waitForMessageFromNode(b: AkonoNativePointer): String
+
+ private var internalNativePointer: AkonoNativePointer
+
+ fun evalJs(source: String): String = evalJs(source, internalNativePointer)
+
+ @Override
+ protected fun finalize() {
+ destroyNative(internalNativePointer)
+ }
+
+ constructor(vararg nodeArgv: String) {
+ internalNativePointer = initNative(nodeArgv)
+ }
+
+ companion object {
+ init {
+ System.loadLibrary("akono-jni")
+ }
+ }
+} \ No newline at end of file
diff --git a/library/src/main/kotlin/akono/Library.kt b/library/src/main/java/akono/Library.kt
index 920648fd..920648fd 100644
--- a/library/src/main/kotlin/akono/Library.kt
+++ b/library/src/main/java/akono/Library.kt
diff --git a/library/src/main/kotlin/akono/AkoniJni.kt b/library/src/main/kotlin/akono/AkoniJni.kt
deleted file mode 100644
index e37bef0e..00000000
--- a/library/src/main/kotlin/akono/AkoniJni.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package akono;
-
-class AkonoJni {
- external fun stringFromJNI(): String;
-
- external fun evalJs(source: String): String;
-
- companion object {
- init {
- System.loadLibrary("akono-jni")
- }
- }
-}
diff --git a/library/src/test/kotlin/akono/LibraryTest.kt b/library/src/test/java/akono/LibraryTest.kt
index 1a16e7e6..1a16e7e6 100644
--- a/library/src/test/kotlin/akono/LibraryTest.kt
+++ b/library/src/test/java/akono/LibraryTest.kt