diff options
Diffstat (limited to 'library/src')
-rw-r--r-- | library/src/androidTest/java/akono/InstrumentedAkonoTests.kt | 30 | ||||
-rw-r--r-- | library/src/androidTest/kotlin/InstrumentedAkonoTests.kt | 27 | ||||
-rw-r--r-- | library/src/main/cpp/CMakeLists.txt | 1 | ||||
-rw-r--r-- | library/src/main/cpp/akono-jni.cpp | 290 | ||||
-rw-r--r-- | library/src/main/java/akono/AkoniJni.kt | 39 | ||||
-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.kt | 13 | ||||
-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 |