0
0
mirror of https://github.com/phpv8/v8js.git synced 2024-11-09 16:28:41 +00:00

Merge pull request #53 from stesie/v8-debug

Add V8 debugging support
This commit is contained in:
Patrick Reilly 2013-10-13 12:28:58 -07:00
commit 17381525f1
7 changed files with 233 additions and 23 deletions

View File

@ -77,8 +77,13 @@ PHP API
/* Constants */
const string V8_VERSION;
const int FLAG_NONE;
const int FLAG_FORCE_ARRAY;
const int DEBUG_AUTO_BREAK_NEVER;
const int DEBUG_AUTO_BREAK_ONCE;
const int DEBUG_AUTO_BREAK_ALWAYS;
/* Methods */
@ -96,6 +101,9 @@ PHP API
// Returns uncaught pending exception or null if there is no pending exception.
public V8JsScriptException V8Js::getPendingException( )
// Starts V8 debug agent for use with Google Chrome Developer Tools (Eclipse Plugin)
public bool startDebugAgent( [ string $agent_name = "V8Js" [, $port = 9222 [, $auto_break = V8Js::DEBUG_AUTO_BREAK_NEVER ] ] ] )
/** Static methods **/
// Registers persistent context independent global Javascript extension.

View File

@ -30,15 +30,16 @@ if test "$PHP_V8JS" != "no"; then
PHP_SUBST(V8JS_SHARED_LIBADD)
PHP_REQUIRE_CXX()
old_LIBS=$LIBS
old_LDFLAGS=$LDFLAGS
old_CPPFLAGS=$CPPFLAGS
LDFLAGS="-Wl,--rpath=$V8_DIR/$PHP_LIBDIR -L$V8_DIR/$PHP_LIBDIR"
LIBS=-lv8
CPPFLAGS=-I$V8_DIR/include
AC_LANG_SAVE
AC_LANG_CPLUSPLUS
AC_CACHE_CHECK(for V8 version, ac_cv_v8_version, [
old_LIBS=$LIBS
old_LDFLAGS=$LDFLAGS
old_CPPFLAGS=$CPPFLAGS
LDFLAGS="-Wl,--rpath=$V8_DIR/$PHP_LIBDIR -L$V8_DIR/$PHP_LIBDIR"
LIBS=-lv8
CPPFLAGS=-I$V8_DIR/include
AC_LANG_SAVE
AC_LANG_CPLUSPLUS
AC_TRY_RUN([#include <v8.h>
#include <iostream>
#include <fstream>
@ -55,10 +56,6 @@ int main ()
}
return 1;
}], [ac_cv_v8_version=`cat ./conftestval|awk '{print $1}'`], [ac_cv_v8_version=NONE], [ac_cv_v8_version=NONE])
AC_LANG_RESTORE
LIBS=$old_LIBS
LDFLAGS=$old_LDFLAGS
CPPFLAGS=$old_CPPFLAGS
])
if test "$ac_cv_v8_version" != "NONE"; then
@ -76,6 +73,23 @@ CPPFLAGS=$old_CPPFLAGS
AC_MSG_ERROR([could not determine libv8 version])
fi
AC_CACHE_CHECK(for debuggersupport in v8, ac_cv_v8_debuggersupport, [
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <v8-debug.h>]],
[[v8::Debug::DisableAgent()]])],
[ac_cv_v8_debuggersupport=yes],
[ac_cv_v8_debuggersupport=no])
])
if test "$ac_cv_v8_debuggersupport" = "yes"; then
AC_DEFINE([ENABLE_DEBUGGER_SUPPORT], [1], [Enable debugger support in V8Js])
fi
AC_LANG_RESTORE
LIBS=$old_LIBS
LDFLAGS=$old_LDFLAGS
CPPFLAGS=$old_CPPFLAGS
AC_CACHE_CHECK(for C standard version, ac_cv_v8_cstd, [
ac_cv_v8_cstd="c++11"
old_CPPFLAGS=$CPPFLAGS

View File

@ -76,6 +76,10 @@ extern "C" {
#define V8JS_FLAG_NONE (1<<0)
#define V8JS_FLAG_FORCE_ARRAY (1<<1)
#define V8JS_DEBUG_AUTO_BREAK_NEVER 0
#define V8JS_DEBUG_AUTO_BREAK_ONCE 1
#define V8JS_DEBUG_AUTO_BREAK_ALWAYS 2
/* Extracts a C string from a V8 Utf8Value. */
static const char * ToCString(const v8::String::Utf8Value &value) /* {{{ */
{

View File

@ -0,0 +1,36 @@
<?php
class LineProcessor {
protected $_processor;
public function readLineLoop() {
$fh = fopen('php://stdin', 'r');
$p = $this->_processor;
while(($line = fgets($fh))) {
echo $p($line);
}
}
public function setProcessor($p) {
$this->_processor = $p;
}
}
$v8 = new V8Js();
$v8->lp = new LineProcessor();
$v8->startDebugAgent('LineProcessor', 9222, V8Js::DEBUG_AUTO_BREAK_NEVER);
$JS = <<< EOT
print("Hello LineProcessor User!\\n");
PHP.lp.setProcessor(function (foo) {
return foo.toUpperCase();
});
PHP.lp.readLineLoop();
EOT;
$v8->executeString($JS, 'processor.js');

View File

@ -0,0 +1,22 @@
<?php
$v8 = new V8Js();
$v8->startDebugAgent('LineProcessor', 9222, V8Js::DEBUG_AUTO_BREAK_ALWAYS);
$JS = <<< EOT
print("Hello LineProcessor User!\\n");
function processLine(foo) {
return foo.toUpperCase();
};
EOT;
$v8->executeString($JS, 'processor.js');
$fh = fopen('php://stdin', 'r');
while(($line = fgets($fh))) {
echo $v8->executeString('processLine('.json_encode($line).');');
}

125
v8js.cc
View File

@ -19,6 +19,8 @@
#include "config.h"
#endif
#include <v8-debug.h>
#include "php_v8js_macros.h"
extern "C" {
@ -101,6 +103,11 @@ struct php_v8js_jsext {
};
/* }}} */
#ifdef ENABLE_DEBUGGER_SUPPORT
static php_v8js_ctx *v8js_debug_context;
static int v8js_debug_auto_break_mode;
#endif
#ifdef COMPILE_DL_V8JS
ZEND_GET_MODULE(v8js)
#endif
@ -598,6 +605,25 @@ static void php_v8js_fatal_error_handler(const char *location, const char *messa
}
/* }}} */
#ifdef ENABLE_DEBUGGER_SUPPORT
static void DispatchDebugMessages() { /* {{{ */
if(v8js_debug_context == NULL) {
return;
}
v8::Isolate* isolate = v8js_debug_context->isolate;
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context =
v8::Local<v8::Context>::New(isolate, v8js_debug_context->context);
v8::Context::Scope scope(context);
v8::Debug::ProcessDebugMessages();
}
/* }}} */
#endif /* ENABLE_DEBUGGER_SUPPORT */
static void php_v8js_init(TSRMLS_D) /* {{{ */
{
/* Run only once */
@ -641,6 +667,9 @@ static PHP_METHOD(V8Js, __construct)
return;
}
/* Initialize V8 */
php_v8js_init(TSRMLS_C);
/* Throw PHP exception if uncaught exceptions exist */
c->report_uncaught = report_uncaught;
c->pending_exception = NULL;
@ -651,9 +680,6 @@ static PHP_METHOD(V8Js, __construct)
c->memory_limit_hit = false;
c->module_loader = NULL;
/* Initialize V8 */
php_v8js_init(TSRMLS_C);
/* Include extensions used by this context */
/* Note: Extensions registered with auto_enable do not need to be added separately like this. */
if (exts_arr)
@ -882,6 +908,17 @@ static PHP_METHOD(V8Js, executeString)
php_v8js_timer_push(time_limit, memory_limit, c TSRMLS_CC);
}
#ifdef ENABLE_DEBUGGER_SUPPORT
if(c == v8js_debug_context && v8js_debug_auto_break_mode != V8JS_DEBUG_AUTO_BREAK_NEVER) {
v8::Debug::DebugBreak(c->isolate);
if(v8js_debug_auto_break_mode == V8JS_DEBUG_AUTO_BREAK_ONCE) {
/* If break-once-mode was enabled, reset flag. */
v8js_debug_auto_break_mode = V8JS_DEBUG_AUTO_BREAK_NEVER;
}
}
#endif /* ENABLE_DEBUGGER_SUPPORT */
/* Execute script */
c->in_execution++;
v8::Local<v8::Value> result = script->Run();
@ -949,6 +986,66 @@ static PHP_METHOD(V8Js, executeString)
}
/* }}} */
#ifdef ENABLE_DEBUGGER_SUPPORT
/* {{{ proto void V8Js::__destruct()
__destruct for V8Js */
static PHP_METHOD(V8Js, __destruct)
{
V8JS_BEGIN_CTX(c, getThis());
if(v8js_debug_context == c) {
v8::Debug::DisableAgent();
v8js_debug_context = NULL;
}
}
/* }}} */
/* {{{ proto bool V8Js::startDebugAgent(string agent_name[, int port[, int auto_break]])
*/
static PHP_METHOD(V8Js, startDebugAgent)
{
char *str = NULL;
int str_len = 0;
long port = 0, auto_break = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sll", &str, &str_len, &port, &auto_break) == FAILURE) {
return;
}
if(!port) {
port = 9222;
}
V8JS_BEGIN_CTX(c, getThis());
if(v8js_debug_context == c) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Debug agent already started for this V8Js instance");
RETURN_BOOL(0);
}
if(v8js_debug_context != NULL) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Debug agent already started for a different V8Js instance");
RETURN_BOOL(0);
}
v8js_debug_context = c;
v8js_debug_auto_break_mode = auto_break;
v8::Debug::SetDebugMessageDispatchHandler(DispatchDebugMessages, true);
v8::Debug::EnableAgent(str_len ? str : "V8Js", port, auto_break > 0);
if(auto_break) {
/* v8::Debug::EnableAgent doesn't really do what we want it to do,
since it only breaks processing on the default isolate.
Hence just trigger another DebugBreak, no for our main isolate. */
v8::Debug::DebugBreak(c->isolate);
}
RETURN_BOOL(1);
}
/* }}} */
#endif /* ENABLE_DEBUGGER_SUPPORT */
/* {{{ proto mixed V8Js::getPendingException()
*/
static PHP_METHOD(V8Js, getPendingException)
@ -1143,6 +1240,17 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_executestring, 0, 0, 1)
ZEND_ARG_INFO(0, memory_limit)
ZEND_END_ARG_INFO()
#ifdef ENABLE_DEBUGGER_SUPPORT
ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_destruct, 0, 0, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_startdebugagent, 0, 0, 0)
ZEND_ARG_INFO(0, agentName)
ZEND_ARG_INFO(0, port)
ZEND_ARG_INFO(0, auto_break)
ZEND_END_ARG_INFO()
#endif /* ENABLE_DEBUGGER_SUPPORT */
ZEND_BEGIN_ARG_INFO(arginfo_v8js_getpendingexception, 0)
ZEND_END_ARG_INFO()
@ -1171,6 +1279,10 @@ static const zend_function_entry v8js_methods[] = { /* {{{ */
PHP_ME(V8Js, setModuleLoader, arginfo_v8js_setmoduleloader, ZEND_ACC_PUBLIC)
PHP_ME(V8Js, registerExtension, arginfo_v8js_registerextension, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
PHP_ME(V8Js, getExtensions, arginfo_v8js_getextensions, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
#ifdef ENABLE_DEBUGGER_SUPPORT
PHP_ME(V8Js, __destruct, arginfo_v8js_destruct, ZEND_ACC_PUBLIC|ZEND_ACC_DTOR)
PHP_ME(V8Js, startDebugAgent, arginfo_v8js_startdebugagent, ZEND_ACC_PUBLIC)
#endif
{NULL, NULL, NULL}
};
/* }}} */
@ -1386,9 +1498,16 @@ static PHP_MINIT_FUNCTION(v8js)
/* V8Js Class Constants */
zend_declare_class_constant_string(php_ce_v8js, ZEND_STRL("V8_VERSION"), PHP_V8_VERSION TSRMLS_CC);
zend_declare_class_constant_long(php_ce_v8js, ZEND_STRL("FLAG_NONE"), V8JS_FLAG_NONE TSRMLS_CC);
zend_declare_class_constant_long(php_ce_v8js, ZEND_STRL("FLAG_FORCE_ARRAY"), V8JS_FLAG_FORCE_ARRAY TSRMLS_CC);
#ifdef ENABLE_DEBUGGER_SUPPORT
zend_declare_class_constant_long(php_ce_v8js, ZEND_STRL("DEBUG_AUTO_BREAK_NEVER"), V8JS_DEBUG_AUTO_BREAK_NEVER TSRMLS_CC);
zend_declare_class_constant_long(php_ce_v8js, ZEND_STRL("DEBUG_AUTO_BREAK_ONCE"), V8JS_DEBUG_AUTO_BREAK_ONCE TSRMLS_CC);
zend_declare_class_constant_long(php_ce_v8js, ZEND_STRL("DEBUG_AUTO_BREAK_ALWAYS"), V8JS_DEBUG_AUTO_BREAK_ALWAYS TSRMLS_CC);
#endif
/* V8JsScriptException Class */
INIT_CLASS_ENTRY(ce, "V8JsScriptException", v8js_script_exception_methods);
php_ce_v8js_script_exception = zend_register_internal_class_ex(&ce, zend_exception_get_default(TSRMLS_C), NULL TSRMLS_CC);

View File

@ -125,15 +125,22 @@ static void php_v8js_call_php_func(zval *value, zend_class_entry *ce, zend_funct
}
fci.no_separation = 1;
/* zend_fcall_info_cache */
fcc.initialized = 1;
fcc.function_handler = method_ptr;
fcc.calling_scope = ce;
fcc.called_scope = ce;
fcc.object_ptr = value;
{
isolate->Exit();
v8::Unlocker unlocker(isolate);
/* Call the method */
zend_call_function(&fci, &fcc TSRMLS_CC);
/* zend_fcall_info_cache */
fcc.initialized = 1;
fcc.function_handler = method_ptr;
fcc.calling_scope = ce;
fcc.called_scope = ce;
fcc.object_ptr = value;
/* Call the method */
zend_call_function(&fci, &fcc TSRMLS_CC);
}
isolate->Enter();
failure:
/* Cleanup */