// v8repack.cpp #include <jni.h> #include <v8.h> #include <libplatform/libplatform.h> #include <string> #include <unordered_map> #include <memory> #include <vector>using namespace v8;
// Structure to hold V8 state struct V8EngineState { std::unique_ptr<Platform> platform; Isolate* isolate; Global<Context> context;
V8EngineState() : isolate(nullptr) {}};
// JNI function implementations extern "C"
JNIEXPORT jlong JNICALL Java_com_v8_repack_V8Repack_initV8(JNIEnv* env, jobject obj) V8EngineState* state = new V8EngineState(); java addon v8 repack
// Initialize V8 platform state->platform = platform::NewDefaultPlatform(); V8::InitializePlatform(state->platform.get()); V8::Initialize(); // Create isolate Isolate::CreateParams create_params; create_params.array_buffer_allocator = ArrayBuffer::Allocator::NewDefaultAllocator(); state->isolate = Isolate::New(create_params); // Create global context Isolate::Scope isolate_scope(state->isolate); HandleScope handle_scope(state->isolate); Local<Context> context = Context::New(state->isolate); state->context.Reset(state->isolate, context); return reinterpret_cast<jlong>(state);JNIEXPORT void JNICALL Java_com_v8_repack_V8Repack_destroyV8(JNIEnv* env, jobject obj, jlong handle) V8EngineState* state = reinterpret_cast<V8EngineState*>(handle); if (state) state->isolate->Dispose(); V8::Dispose(); V8::ShutdownPlatform(); delete state;
JNIEXPORT void JNICALL Java_com_v8_repack_V8Repack_setV8Flags(JNIEnv* env, jobject obj, jlong handle, jstring flags) V8EngineState* state = reinterpret_cast<V8EngineState*>(handle); if (!state) return;
const char* flags_str = env->GetStringUTFChars(flags, nullptr); V8::SetFlagsFromString(flags_str, strlen(flags_str)); env->ReleaseStringUTFChars(flags, flags_str);JNIEXPORT jbyteArray JNICALL Java_com_v8_repack_V8Repack_repackScript( JNIEnv* env, jobject obj, jlong handle, jbyteArray script, jobject dependencies)
V8EngineState* state = reinterpret_cast<V8EngineState*>(handle); if (!state) return nullptr; Isolate::Scope isolate_scope(state->isolate); HandleScope handle_scope(state->isolate); Local<Context> context = state->context.Get(state->isolate); Context::Scope context_scope(context); // Get script string jsize script_len = env->GetArrayLength(script); jbyte* script_bytes = env->GetByteArrayElements(script, nullptr); std::string script_str(reinterpret_cast<char*>(script_bytes), script_len); env->ReleaseByteArrayElements(script, script_bytes, JNI_ABORT); // Build combined script with dependencies std::string combined_script = "// Repacked V8 Script\n"; // Add dependencies as modules if (dependencies != nullptr) jclass mapClass = env->GetObjectClass(dependencies); jmethodID entrySetMethod = env->GetMethodID(mapClass, "entrySet", "()Ljava/util/Set;"); jobject entrySet = env->CallObjectMethod(dependencies, entrySetMethod); jclass setClass = env->GetObjectClass(entrySet); jmethodID iteratorMethod = env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;"); jobject iterator = env->CallObjectMethod(entrySet, iteratorMethod); jclass iteratorClass = env->GetObjectClass(iterator); jmethodID hasNextMethod = env->GetMethodID(iteratorClass, "hasNext", "()Z"); jmethodID nextMethod = env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;"); jclass entryClass = env->FindClass("java/util/Map$Entry"); jmethodID getKeyMethod = env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;"); jmethodID getValueMethod = env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;"); while (env->CallBooleanMethod(iterator, hasNextMethod)) jobject entry = env->CallObjectMethod(iterator, nextMethod); jstring key = (jstring)env->CallObjectMethod(entry, getKeyMethod); jbyteArray value = (jbyteArray)env->CallObjectMethod(entry, getValueMethod); const char* key_str = env->GetStringUTFChars(key, nullptr); jsize value_len = env->GetArrayLength(value); jbyte* value_bytes = env->GetByteArrayElements(value, nullptr); combined_script += "// Module: " + std::string(key_str) + "\n"; combined_script += std::string(reinterpret_cast<char*>(value_bytes), value_len); combined_script += "\n\n"; env->ReleaseStringUTFChars(key, key_str); env->ReleaseByteArrayElements(value, value_bytes, JNI_ABORT); // Add main script combined_script += "// Main script\n"; combined_script += script_str; // Compile and serialize the script Local<String> source = String::NewFromUtf8(state->isolate, combined_script.c_str(), NewStringType::kNormal) .ToLocalChecked(); Local<Script> compiled_script; if (!Script::Compile(context, source).ToLocal(&compiled_script)) return nullptr; // Serialize the compiled script ScriptCompiler::CachedData* cache = ScriptCompiler::CreateCodeCache( compiled_script->GetUnboundScript()); // Create byte array to return jbyteArray result = env->NewByteArray(cache->length); env->SetByteArrayRegion(result, 0, cache->length, reinterpret_cast<jbyte*>(cache->data)); delete cache; return result;JNIEXPORT jbyteArray JNICALL Java_com_v8_repack_V8Repack_executeScript( JNIEnv* env, jobject obj, jlong handle, jbyteArray script)
V8EngineState* state = reinterpret_cast<V8EngineState*>(handle); if (!state) return nullptr; Isolate::Scope isolate_scope(state->isolate); HandleScope handle_scope(state->isolate); Local<Context> context = state->context.Get(state->isolate); Context::Scope context_scope(context); // Get script string jsize script_len = env->GetArrayLength(script); jbyte* script_bytes = env->GetByteArrayElements(script, nullptr); std::string script_str(reinterpret_cast<char*>(script_bytes), script_len); env->ReleaseByteArrayElements(script, script_bytes, JNI_ABORT); // Try to deserialize first ScriptCompiler::CachedData* cache = new ScriptCompiler::CachedData( reinterpret_cast<const uint8_t*>(script_str.c_str()), script_str.length(), ScriptCompiler::CachedData::BufferNotOwned); Local<Script> compiled_script; TryCatch try_catch(state->isolate); // Compile from cache or source if (cache->rejected) Local<String> source = String::NewFromUtf8(state->isolate, script_str.c_str(), NewStringType::kNormal) .ToLocalChecked(); if (!Script::Compile(context, source).ToLocal(&compiled_script)) delete cache; return nullptr; else ScriptCompiler::Source source(cache); if (!ScriptCompiler::Compile(context, &source, ScriptCompiler::kConsumeCodeCache) .ToLocal(&compiled_script)) delete cache; return nullptr; delete cache; // Execute script Local<Value> result; if (!compiled_script->Run(context).ToLocal(&result)) String::Utf8Value error(state->isolate, try_catch.Exception()); return nullptr; // Convert result to string String::Utf8Value utf8(state->isolate, result); std::string result_str(*utf8, utf8.length()); // Return as byte array jbyteArray result_array = env->NewByteArray(result_str.length()); env->SetByteArrayRegion(result_array, 0, result_str.length(), reinterpret_cast<const jbyte*>(result_str.c_str())); return result_array;
// extern "C"
If off-the-shelf solutions like J2V8 lack features or platform support, you may want to create your own repack. This is complex but doable:
gn and ninja.libv8_jni.so)..so and the Java classes into a single JAR.Tools to help: Use javah (or the modern native keyword in Panama) to generate headers, and consider using GraalVM Native Image for a different approach. Java V8 Repack Addon
Typical components