Skip to content

Commit

Permalink
Add debug support to J2V8
Browse files Browse the repository at this point in the history
Debug Support allows the user to specify a debug port on which to attach
an external debugger. The API require the user to register a callback,
which will be invoked whenever a debugger is attached. It is the
responsibility of this callback to invoke V8.processDebugMessages() from
the proper V8 Thread.

Debug support can be registered with an option to halt the VM on the
first instruction and wait for a debugger to connect.
  • Loading branch information
irbull committed Sep 3, 2014
1 parent 10a81cf commit bcce63c
Show file tree
Hide file tree
Showing 8 changed files with 253 additions and 12 deletions.
77 changes: 73 additions & 4 deletions jni/com_eclipsesource_v8_V8Impl.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <jni.h>
#include <iostream>
#include <v8-debug.h>
#include <v8.h>
#include <map>
#include "com_eclipsesource_v8_V8Impl.h"
Expand All @@ -20,16 +21,86 @@ class V8Runtime {
};

std::map <int, V8Runtime*> v8Isolates;
JavaVM* jvm = NULL;

void throwError( JNIEnv *env, const char *message );
void throwExecutionException( JNIEnv *env, const char *message );
void throwResultUndefinedException( JNIEnv *env, const char *message );
Isolate* getIsolate(JNIEnv *env, int handle);
void setupJNIContext(int v8RuntimeHandle, JNIEnv *env, jobject v8 );

void debugHandler() {
JNIEnv * g_env;
// double check it's all ok
int getEnvStat = jvm->GetEnv((void **) &g_env, JNI_VERSION_1_6);
if (getEnvStat == JNI_EDETACHED) {
if (jvm->AttachCurrentThread((void **) &g_env, NULL) != 0) {
std::cout << "Failed to attach" << std::endl;
}
} else if (getEnvStat == JNI_OK) {
//
} else if (getEnvStat == JNI_EVERSION) {
std::cout << "GetEnv: version not supported" << std::endl;
}

jclass cls = g_env->FindClass("com/eclipsesource/v8/V8");
jmethodID processDebugMessage = g_env->GetStaticMethodID(cls, "debugMessageReceived", "()V");

g_env->CallStaticVoidMethod(cls, processDebugMessage);

if (g_env->ExceptionCheck()) {
g_env->ExceptionDescribe();
}

jvm->DetachCurrentThread();
}

JNIEXPORT jboolean JNICALL Java_com_eclipsesource_v8_V8__1enableDebugSupport
(JNIEnv *env, jobject, jint v8RuntimeHandle, jint port, jboolean waitForConnection) {
Isolate* isolate = getIsolate(env, v8RuntimeHandle);
if ( isolate == NULL ) {
return false;
}
v8::Isolate::Scope isolateScope(isolate);
HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Local<v8::Context>::New(isolate,v8Isolates[v8RuntimeHandle]->context_);
Context::Scope context_scope(context);
bool result = v8::Debug::EnableAgent("j2v8", port, waitForConnection);
v8::Debug::SetDebugMessageDispatchHandler(&debugHandler);
return result;
}

JNIEXPORT void JNICALL Java_com_eclipsesource_v8_V8__1disableDebugSupport
(JNIEnv *env, jobject, jint v8RuntimeHandle) {
Isolate* isolate = getIsolate(env, v8RuntimeHandle);
if ( isolate == NULL ) {
return;
}
v8::Isolate::Scope isolateScope(isolate);
HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Local<v8::Context>::New(isolate,v8Isolates[v8RuntimeHandle]->context_);
Context::Scope context_scope(context);
v8::Debug::DisableAgent();
}

JNIEXPORT void JNICALL Java_com_eclipsesource_v8_V8__1processDebugMessages
(JNIEnv *env, jobject, jint v8RuntimeHandle) {
Isolate* isolate = getIsolate(env, v8RuntimeHandle);
if ( isolate == NULL ) {
return;
}
v8::Isolate::Scope isolateScope(isolate);
HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Local<v8::Context>::New(isolate,v8Isolates[v8RuntimeHandle]->context_);
Context::Scope context_scope(context);
v8::Debug::ProcessDebugMessages();
}

JNIEXPORT void JNICALL Java_com_eclipsesource_v8_V8__1createIsolate
(JNIEnv *, jobject, jint handle) {
(JNIEnv *env, jobject, jint handle) {
if (jvm == NULL ) {
env->GetJavaVM(&jvm);
}
v8Isolates[handle] = new V8Runtime();
v8Isolates[handle]->isolate = Isolate::New();
v8Isolates[handle]->isolate_scope = new Isolate::Scope(v8Isolates[handle]->isolate);
Expand Down Expand Up @@ -613,7 +684,6 @@ JNIEXPORT void JNICALL Java_com_eclipsesource_v8_V8__1executeVoidFunction
v8::Local<v8::Context> context = v8::Local<v8::Context>::New(isolate,v8Isolates[v8RuntimeHandle]->context_);
Context::Scope context_scope(context);
Handle<v8::Object> parentObject = Local<Object>::New(isolate, *v8Isolates[v8RuntimeHandle]->objects[objectHandle]);

int size = 0;
Handle<Value>* args = NULL;
if ( parameterHandle >= 0 ) {
Expand All @@ -624,8 +694,7 @@ JNIEXPORT void JNICALL Java_com_eclipsesource_v8_V8__1executeVoidFunction
args[i] = parameters->Get(i);
}
}

Handle<v8::Value> value = parentObject->Get(v8::String::NewFromUtf8(v8::Isolate::GetCurrent(), functionName));
Handle<v8::Value> value = parentObject->Get(v8::String::NewFromUtf8(isolate, functionName));
Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(value);
func->Call(parentObject, size, args);
env->ReleaseStringUTFChars(jfunctionName, functionName);
Expand Down
24 changes: 24 additions & 0 deletions jni/com_eclipsesource_v8_V8Impl.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 66 additions & 8 deletions src/main/java/com/eclipsesource/v8/V8.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class V8 extends V8Object {

private static int v8InstanceCounter;
private int methodReferenceCounter = 0;
private static Thread thread = null;
private static List<V8> runtimes = new ArrayList<>();
private static Runnable debugHandler = null;

private int v8RuntimeHandle;
static Thread thread = null;
long objectReferences = 0;
private int methodReferenceCounter = 0;
private int v8RuntimeHandle;
private boolean debugEnabled = false;
long objectReferences = 0;

class MethodDescriptor {
Object object;
Expand All @@ -29,28 +34,69 @@ public synchronized static V8 createV8Runtime() {
if (thread == null) {
thread = Thread.currentThread();
}
return new V8();
V8 runtime = new V8();
runtimes.add(runtime);
return runtime;
}

private V8() {
protected V8() {
checkThread();
v8RuntimeHandle = v8InstanceCounter++;
_createIsolate(v8RuntimeHandle);
}

public boolean enableDebugSupport(final int port, final boolean waitForConnection) {
checkThread();
debugEnabled = _enableDebugSupport(getHandle(), port, waitForConnection);
return debugEnabled;
}

public boolean enableDebugSupport(final int port) {
checkThread();
debugEnabled = true;
debugEnabled = _enableDebugSupport(getV8RuntimeHandle(), port, false);
return debugEnabled;
}

public void disableDebugSupport() {
checkThread();
_disableDebugSupport(getV8RuntimeHandle());
debugEnabled = false;
}

public static void processDebugMessages() {
checkThread();
for (V8 v8 : runtimes) {
v8._processDebugMessages(v8.getV8RuntimeHandle());
}
}

public static int getActiveRuntimes() {
return runtimes.size();
}

public static void registerDebugHandler(final Runnable handler) {
debugHandler = handler;
}

public int getV8RuntimeHandle() {
return v8RuntimeHandle;
}

@Override
public void release() {
checkThread();
if (debugEnabled) {
disableDebugSupport();
}
runtimes.remove(this);
_releaseRuntime(v8RuntimeHandle);
if (objectReferences > 0) {
throw new IllegalStateException(objectReferences + " Object(s) still exist in runtime");
}
_releaseRuntime(v8RuntimeHandle);
}


public int executeIntScript(final String script) throws V8RuntimeException {
checkThread();
return _executeIntScript(v8RuntimeHandle, script);
Expand Down Expand Up @@ -101,7 +147,7 @@ public void executeVoidScript(final String script) throws V8RuntimeException {
}

static void checkThread() {
if (thread != Thread.currentThread()) {
if ((thread != null) && (thread != Thread.currentThread())) {
throw new Error("Invalid V8 thread access.");
}
}
Expand Down Expand Up @@ -163,6 +209,12 @@ private Object getArrayItem(final V8Array array, final int index) {
return null;
}

protected static void debugMessageReceived() {
if (debugHandler != null) {
debugHandler.run();
}
}

protected native void _initExistingV8Object(int v8RuntimeHandle, int parentHandle, String objectKey,
int objectHandle);

Expand Down Expand Up @@ -275,6 +327,12 @@ protected native void _arrayGetObject(final int v8RuntimeHandle, final int array

protected native void _setPrototype(int v8RuntimeHandle, int objectHandle, int prototypeHandle);

protected native boolean _enableDebugSupport(int v8RuntimeHandle, int port, boolean waitForConnection);

protected native void _disableDebugSupport(int v8RuntimeHandle);

protected native void _processDebugMessages(int v8RuntimeHandle);

void addObjRef() {
objectReferences++;
}
Expand Down
3 changes: 3 additions & 0 deletions src/test/java/com/eclipsesource/v8/tests/V8ArrayTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ public void seutp() {
public void tearDown() {
try {
v8.release();
if (V8.getActiveRuntimes() != 0) {
throw new IllegalStateException("V8Runtimes not properly released.");
}
} catch (IllegalStateException e) {
System.out.println(e.getMessage());
}
Expand Down
3 changes: 3 additions & 0 deletions src/test/java/com/eclipsesource/v8/tests/V8CallbackTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ public void seutp() {
public void tearDown() {
try {
v8.release();
if (V8.getActiveRuntimes() != 0) {
throw new IllegalStateException("V8Runtimes not properly released.");
}
} catch (IllegalStateException e) {
System.out.println(e.getMessage());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ public void seutp() {
public void tearDown() {
try {
v8.release();
if (V8.getActiveRuntimes() != 0) {
throw new IllegalStateException("V8Runtimes not properly released.");
}
} catch (IllegalStateException e) {
System.out.println(e.getMessage());
}
Expand Down
3 changes: 3 additions & 0 deletions src/test/java/com/eclipsesource/v8/tests/V8ObjectTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ public void seutp() {
public void tearDown() {
try {
v8.release();
if (V8.getActiveRuntimes() != 0) {
throw new IllegalStateException("V8Runtimes not properly released.");
}
} catch (IllegalStateException e) {
System.out.println(e.getMessage());
}
Expand Down
Loading

0 comments on commit bcce63c

Please sign in to comment.