Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
BasedOnStyle: Google
ColumnLimit: 0
8 changes: 4 additions & 4 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ jobs:
e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- id: yarn-cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
**/node_modules
Expand All @@ -22,11 +22,11 @@ jobs:
restore-keys: |
${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}
${{ runner.os }}-yarn-
- uses: actions/setup-java@v3
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
- uses: android-actions/setup-android@v2
- uses: android-actions/setup-android@v3
- uses: nttld/setup-ndk@v1
id: setup-ndk
with:
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ name: iOS

on:
workflow_dispatch:
pull_request:
push:
tags:
- 'v*'
# pull_request:
# push:
# tags:
# - 'v*'

jobs:
e2e:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- id: yarn-cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
**/node_modules
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/npm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '16'
node-version: '22'
registry-url: https://registry.npmjs.org/
- run: yarn install
- run: npm publish --access public
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: scottbrenner/generate-changelog-action@master
Expand Down
141 changes: 76 additions & 65 deletions android/fast-openpgp-adapter.cpp
Original file line number Diff line number Diff line change
@@ -1,83 +1,94 @@
#include <jni.h>
#include "react-native-fast-openpgp.h"
#include <android/log.h>
#include <jni.h>
#include <libopenpgp_bridge.h>

extern "C"
JNIEXPORT void JNICALL
Java_com_fastopenpgp_FastOpenpgpModule_initialize(JNIEnv *env, jobject thiz,
jlong jsi_ptr) {
__android_log_print(ANDROID_LOG_VERBOSE, "react-native-fast-openpgp",
"Initializing");
fastOpenPGP::install(*reinterpret_cast<facebook::jsi::Runtime *>(jsi_ptr));
}

extern "C"
JNIEXPORT void JNICALL
Java_com_fastopenpgp_FastOpenpgpModule_destruct(JNIEnv *env, jobject thiz) {
fastOpenPGP::cleanup();
}
extern "C"
JNIEXPORT jbyteArray JNICALL
Java_com_fastopenpgp_FastOpenpgpModule_callNative(JNIEnv *env, jobject thiz,
jstring name, jbyteArray payload) {
#include "react-native-fast-openpgp.h"

auto nameConstChar = env->GetStringUTFChars(name, nullptr);
auto payloadBytes = env->GetByteArrayElements(payload, nullptr);
auto size = env->GetArrayLength(payload);
extern "C" JNIEXPORT void JNICALL
Java_com_fastopenpgp_FastOpenpgpModule_initialize(JNIEnv* env,
jobject /* thiz */,
jlong jsContext) {
if (jsContext == 0) {
__android_log_print(ANDROID_LOG_ERROR, "react-native-fast-openpgp", "Failed to initialize: jsContext is null");
jclass Exception = env->FindClass("java/lang/IllegalArgumentException");
env->ThrowNew(Exception, "JSI context is null");
return;
}

auto nameChar = const_cast<char *>(nameConstChar);
auto response = OpenPGPBridgeCall(nameChar, payloadBytes, size);
__android_log_print(ANDROID_LOG_VERBOSE, "react-native-fast-openpgp", "Initializing JSI bindings");

env->ReleaseStringUTFChars(name, nameConstChar);
env->ReleaseByteArrayElements(payload, payloadBytes, 0);
try {
auto* runtime = reinterpret_cast<facebook::jsi::Runtime*>(jsContext);

if (response->error != nullptr) {
auto error = response->error;
free(response);
jclass Exception = env->FindClass("java/lang/Exception");
env->ThrowNew(Exception, error);
return nullptr;
}
fastOpenPGP::install(*runtime);

auto result = env->NewByteArray(response->size);
env->SetByteArrayRegion(result, 0, response->size, (jbyte*) response->message);
free(response);
return result;
__android_log_print(ANDROID_LOG_INFO, "react-native-fast-openpgp", "JSI bindings successfully installed");
} catch (const std::exception& e) {
__android_log_print(ANDROID_LOG_ERROR, "react-native-fast-openpgp", "Exception during initialization: %s", e.what());
jclass Exception = env->FindClass("java/lang/RuntimeException");
env->ThrowNew(Exception, e.what());
} catch (...) {
__android_log_print(ANDROID_LOG_ERROR, "react-native-fast-openpgp", "Unknown error during initialization");
jclass Exception = env->FindClass("java/lang/RuntimeException");
env->ThrowNew(Exception, "Unknown error occurred during JSI initialization");
}
}
extern "C" JNIEXPORT void JNICALL
Java_com_fastopenpgp_FastOpenpgpModule_destruct(JNIEnv* env, jobject thiz) {
fastOpenPGP::cleanup();
}
extern "C" JNIEXPORT jbyteArray JNICALL
Java_com_fastopenpgp_FastOpenpgpModule_callNative(JNIEnv* env,
jobject thiz,
jstring name,
jbyteArray payload) {
if (name == nullptr || payload == nullptr) {
jclass Exception = env->FindClass("java/lang/NullPointerException");
env->ThrowNew(Exception, "Input parameters 'name' or 'payload' cannot be null");
return nullptr;
}

const char* nameConstChar = env->GetStringUTFChars(name, nullptr);
if (nameConstChar == nullptr) {
jclass Exception = env->FindClass("java/lang/OutOfMemoryError");
env->ThrowNew(Exception, "Failed to allocate memory for 'name'");
return nullptr;
}

extern "C"
JNIEXPORT jbyteArray JNICALL
Java_com_fastopenpgp_FastOpenpgpModule_callJSI(JNIEnv *env, jobject thiz, jlong jsi_ptr,
jstring name, jbyteArray payload) {
auto &runtime = *reinterpret_cast<jsi::Runtime *>(jsi_ptr);
auto nameConstChar = env->GetStringUTFChars(name, nullptr);
auto payloadBytes = env->GetByteArrayElements(payload, nullptr);
auto size = env->GetArrayLength(payload);

auto nameValue = jsi::String::createFromAscii(runtime, nameConstChar);
jbyte* payloadBytes = env->GetByteArrayElements(payload, nullptr);
if (payloadBytes == nullptr) {
env->ReleaseStringUTFChars(name, nameConstChar);
jclass Exception = env->FindClass("java/lang/OutOfMemoryError");
env->ThrowNew(Exception, "Failed to allocate memory for 'payload'");
return nullptr;
}

jsize size = env->GetArrayLength(payload);
auto response =
OpenPGPBridgeCall(const_cast<char*>(nameConstChar), payloadBytes, size);

auto arrayBuffer = runtime.global().getPropertyAsFunction(runtime, "ArrayBuffer");
jsi::Object o = arrayBuffer.callAsConstructor(runtime, size).getObject(runtime);
jsi::ArrayBuffer payloadValue = o.getArrayBuffer(runtime);
memcpy(payloadValue.data(runtime), payloadBytes, size);
env->ReleaseByteArrayElements(payload, payloadBytes, 0);
// Release resources
env->ReleaseStringUTFChars(name, nameConstChar);
env->ReleaseByteArrayElements(payload, payloadBytes, JNI_ABORT);

auto response = fastOpenPGP::call(runtime, nameValue, payloadValue);
if (response->error != nullptr) {
const char* error = response->error;
free(response);
jclass Exception = env->FindClass("java/lang/Exception");
env->ThrowNew(Exception, error);
return nullptr;
}

jbyteArray result = env->NewByteArray(response->size);
if (result == nullptr) {
free(response);
jclass Exception = env->FindClass("java/lang/OutOfMemoryError");
env->ThrowNew(Exception, "Failed to allocate memory for result");
return nullptr;
}

if (response.isString()) {
auto error = response.asString(runtime);
jclass Exception = env->FindClass("java/lang/Exception");
env->ThrowNew(Exception, error.utf8(runtime).c_str());
return nullptr;
env->SetByteArrayRegion(result, 0, response->size, reinterpret_cast<jbyte*>(response->message));
free(response);

}
auto byteResult = response.asObject(runtime).getArrayBuffer(runtime);
auto sizeResult = byteResult.size(runtime);
auto result = env->NewByteArray(sizeResult);
env->SetByteArrayRegion(result, 0, sizeResult, (jbyte*) byteResult.data(runtime));
return result;
return result;
}
6 changes: 3 additions & 3 deletions android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
FastOpenpgp_kotlinVersion=1.7.0
FastOpenpgp_minSdkVersion=21
FastOpenpgp_targetSdkVersion=31
FastOpenpgp_compileSdkVersion=31
FastOpenpgp_ndkversion=21.4.7075529
FastOpenpgp_targetSdkVersion=33
FastOpenpgp_compileSdkVersion=33
FastOpenpgp_ndkversion=23.1.7779620
68 changes: 23 additions & 45 deletions android/src/main/java/com/fastopenpgp/FastOpenpgpModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ internal class FastOpenpgpModule(reactContext: ReactApplicationContext) :

val TAG = "[FastOpenPGPModule]"

external fun initialize(jsiPtr: Long);
external fun initialize(jsContext: Long)
external fun destruct();
external fun callJSI(jsiPtr: Long, name: String, payload: ByteArray): ByteArray;
external fun callNative(name: String, payload: ByteArray): ByteArray;

companion object {
Expand All @@ -20,64 +19,43 @@ internal class FastOpenpgpModule(reactContext: ReactApplicationContext) :
}
}

@ReactMethod
fun callJSI(name: String, payload: ReadableArray, promise: Promise) {
Thread {
reactApplicationContext.runOnJSQueueThread {
try {
val contextHolder = this.reactApplicationContext.javaScriptContextHolder!!.get()
if (contextHolder.toInt() == 0) {
call(name, payload, promise)
return@runOnJSQueueThread
}
val bytes = ByteArray(payload.size()) { pos -> payload.getInt(pos).toByte() }
val result = callJSI(contextHolder, name, bytes)
val resultList = Arguments.createArray()
for (i in result.indices) {
resultList.pushInt(result[i].toInt())
}
promise.resolve(resultList)
} catch (e: Exception) {
promise.reject(e)
}
}
}.start()
}

@ReactMethod
fun call(name: String, payload: ReadableArray, promise: Promise) {
Thread {
try {
val bytes = ByteArray(payload.size()) { pos -> payload.getInt(pos).toByte() }
val bytes = ByteArray(payload.size()) { index ->
payload.getInt(index).toByte()
}
val result = callNative(name, bytes)
val resultList = Arguments.createArray()
for (i in result.indices) {
resultList.pushInt(result[i].toInt())
val resultList = Arguments.createArray().apply {
result.forEach { pushInt(it.toInt()) }
}

promise.resolve(resultList)
} catch (e: Exception) {
promise.reject(e)
promise.reject("CALL_ERROR", "An error occurred during native call", e)
}
}.start()
}

@ReactMethod(isBlockingSynchronousMethod = true)
fun install(): Boolean {
Log.d(TAG, "installing")
try {
val contextHolder = this.reactApplicationContext.javaScriptContextHolder!!.get()
if (contextHolder.toInt() == 0) {
Log.d(TAG, "context not available")
return false
}
initialize(contextHolder)
Log.i(TAG, "successfully installed")
return true
} catch (exception: java.lang.Exception) {
Log.e(TAG, "failed to install JSI", exception)
return false
Log.d(TAG, "Attempting to install JSI bindings...")
return try {
val contextHolder = reactApplicationContext.javaScriptContextHolder?.get()
if (contextHolder == null || contextHolder.toInt() == 0) {
Log.w(TAG, "JSI context is not available")
false
} else {
initialize(contextHolder)
Log.i(TAG, "JSI bindings successfully installed")
true
}
} catch (e: Exception) {
Log.e(TAG, "Failed to install JSI bindings", e)
false
}
}
}

override fun getName(): String {
return "FastOpenpgp"
Expand Down
Binary file modified android/src/main/jniLibs/arm64-v8a/libopenpgp_bridge.so
Binary file not shown.
Binary file modified android/src/main/jniLibs/armeabi-v7a/libopenpgp_bridge.so
Binary file not shown.
Binary file modified android/src/main/jniLibs/x86/libopenpgp_bridge.so
Binary file not shown.
Binary file modified android/src/main/jniLibs/x86_64/libopenpgp_bridge.so
Binary file not shown.
6 changes: 5 additions & 1 deletion cpp/libopenpgp_bridge.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#include <stdint.h>
#include <stdlib.h>
typedef struct { void* message; int size; char* error; } BytesReturn;
typedef struct {
void* message;
int size;
char* error;
} BytesReturn;

#ifdef __cplusplus
extern "C" {
Expand Down
Loading