Home | History | Annotate | Download | only in v8
      1 /*
      2  * Copyright (c) 2010-2011 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #include "bindings/v8/ScriptDebugServer.h"
     33 
     34 #include "DebuggerScriptSource.h"
     35 #include "V8JavaScriptCallFrame.h"
     36 #include "bindings/v8/ScopedPersistent.h"
     37 #include "bindings/v8/ScriptObject.h"
     38 #include "bindings/v8/V8Binding.h"
     39 #include "bindings/v8/V8ScriptRunner.h"
     40 #include "core/inspector/JavaScriptCallFrame.h"
     41 #include "core/inspector/ScriptDebugListener.h"
     42 #include "wtf/StdLibExtras.h"
     43 #include "wtf/Vector.h"
     44 #include "wtf/dtoa/utils.h"
     45 #include "wtf/text/CString.h"
     46 
     47 namespace WebCore {
     48 
     49 namespace {
     50 
     51 class ClientDataImpl : public v8::Debug::ClientData {
     52 public:
     53     ClientDataImpl(PassOwnPtr<ScriptDebugServer::Task> task) : m_task(task) { }
     54     virtual ~ClientDataImpl() { }
     55     ScriptDebugServer::Task* task() const { return m_task.get(); }
     56 private:
     57     OwnPtr<ScriptDebugServer::Task> m_task;
     58 };
     59 
     60 const char stepIntoV8MethodName[] = "stepIntoStatement";
     61 const char stepOutV8MethodName[] = "stepOutOfFunction";
     62 }
     63 
     64 v8::Local<v8::Value> ScriptDebugServer::callDebuggerMethod(const char* functionName, int argc, v8::Handle<v8::Value> argv[])
     65 {
     66     v8::Handle<v8::Object> debuggerScript = m_debuggerScript.newLocal(m_isolate);
     67     v8::Handle<v8::Function> function = v8::Local<v8::Function>::Cast(debuggerScript->Get(v8::String::NewSymbol(functionName)));
     68     ASSERT(v8::Context::InContext());
     69     return V8ScriptRunner::callInternalFunction(function, debuggerScript, argc, argv, m_isolate);
     70 }
     71 
     72 class ScriptDebugServer::ScriptPreprocessor {
     73     WTF_MAKE_NONCOPYABLE(ScriptPreprocessor);
     74 public:
     75     ScriptPreprocessor(const String& preprocessorScript, v8::Isolate* isolate)
     76         : m_isolate(isolate)
     77     {
     78         v8::HandleScope scope(m_isolate);
     79 
     80         v8::Local<v8::Context> context = v8::Context::New(m_isolate);
     81         if (context.IsEmpty())
     82             return;
     83         v8::Context::Scope contextScope(context);
     84 
     85         String wrappedScript = "(" + preprocessorScript + ")";
     86         v8::Handle<v8::String> preprocessor = v8::String::New(wrappedScript.utf8().data(), wrappedScript.utf8().length());
     87 
     88         v8::Local<v8::Value> preprocessorFunction = V8ScriptRunner::compileAndRunInternalScript(preprocessor, m_isolate);
     89         if (preprocessorFunction.IsEmpty() || !preprocessorFunction->IsFunction())
     90             return;
     91 
     92         m_utilityContext.set(isolate, context);
     93         m_preprocessorFunction.set(isolate, v8::Handle<v8::Function>::Cast(preprocessorFunction));
     94     }
     95 
     96     String preprocessSourceCode(const String& sourceCode, const String& sourceName)
     97     {
     98         v8::HandleScope handleScope(m_isolate);
     99 
    100         if (m_preprocessorFunction.isEmpty())
    101             return sourceCode;
    102 
    103         v8::Local<v8::Context> context = m_utilityContext.newLocal(m_isolate);
    104         v8::Context::Scope contextScope(context);
    105 
    106         v8::Handle<v8::String> sourceCodeString = v8::String::New(sourceCode.utf8().data(), sourceCode.utf8().length());
    107 
    108         v8::Handle<v8::String> sourceNameString = v8::String::New(sourceName.utf8().data(), sourceName.utf8().length());
    109         v8::Handle<v8::Value> argv[] = { sourceCodeString, sourceNameString };
    110         v8::Handle<v8::Value> resultValue = V8ScriptRunner::callInternalFunction(m_preprocessorFunction.newLocal(m_isolate), context->Global(), WTF_ARRAY_LENGTH(argv), argv, m_isolate);
    111 
    112         if (!resultValue.IsEmpty() && resultValue->IsString()) {
    113             v8::String::Utf8Value utf8Value(resultValue);
    114             return String::fromUTF8(*utf8Value, utf8Value.length());
    115         }
    116         return sourceCode;
    117     }
    118 
    119     ~ScriptPreprocessor()
    120     {
    121     }
    122 
    123 private:
    124     ScopedPersistent<v8::Context> m_utilityContext;
    125     String m_preprocessorBody;
    126     ScopedPersistent<v8::Function> m_preprocessorFunction;
    127     v8::Isolate* m_isolate;
    128 };
    129 
    130 ScriptDebugServer::ScriptDebugServer(v8::Isolate* isolate)
    131     : m_pauseOnExceptionsState(DontPauseOnExceptions)
    132     , m_breakpointsActivated(true)
    133     , m_runningNestedMessageLoop(false)
    134     , m_isolate(isolate)
    135 {
    136 }
    137 
    138 ScriptDebugServer::~ScriptDebugServer()
    139 {
    140 }
    141 
    142 String ScriptDebugServer::setBreakpoint(const String& sourceID, const ScriptBreakpoint& scriptBreakpoint, int* actualLineNumber, int* actualColumnNumber, bool interstatementLocation)
    143 {
    144     v8::HandleScope scope(m_isolate);
    145     v8::Local<v8::Context> debuggerContext = v8::Debug::GetDebugContext();
    146     v8::Context::Scope contextScope(debuggerContext);
    147 
    148     v8::Local<v8::Object> args = v8::Object::New();
    149     args->Set(v8::String::NewSymbol("sourceID"), v8String(sourceID, debuggerContext->GetIsolate()));
    150     args->Set(v8::String::NewSymbol("lineNumber"), v8::Integer::New(scriptBreakpoint.lineNumber, debuggerContext->GetIsolate()));
    151     args->Set(v8::String::NewSymbol("columnNumber"), v8::Integer::New(scriptBreakpoint.columnNumber, debuggerContext->GetIsolate()));
    152     args->Set(v8::String::NewSymbol("interstatementLocation"), v8Boolean(interstatementLocation, debuggerContext->GetIsolate()));
    153     args->Set(v8::String::NewSymbol("condition"), v8String(scriptBreakpoint.condition, debuggerContext->GetIsolate()));
    154 
    155     v8::Handle<v8::Function> setBreakpointFunction = v8::Local<v8::Function>::Cast(m_debuggerScript.newLocal(m_isolate)->Get(v8::String::NewSymbol("setBreakpoint")));
    156     v8::Handle<v8::Value> breakpointId = v8::Debug::Call(setBreakpointFunction, args);
    157     if (!breakpointId->IsString())
    158         return "";
    159     *actualLineNumber = args->Get(v8::String::NewSymbol("lineNumber"))->Int32Value();
    160     *actualColumnNumber = args->Get(v8::String::NewSymbol("columnNumber"))->Int32Value();
    161     return toWebCoreString(breakpointId->ToString());
    162 }
    163 
    164 void ScriptDebugServer::removeBreakpoint(const String& breakpointId)
    165 {
    166     v8::HandleScope scope(m_isolate);
    167     v8::Local<v8::Context> debuggerContext = v8::Debug::GetDebugContext();
    168     v8::Context::Scope contextScope(debuggerContext);
    169 
    170     v8::Local<v8::Object> args = v8::Object::New();
    171     args->Set(v8::String::NewSymbol("breakpointId"), v8String(breakpointId, debuggerContext->GetIsolate()));
    172 
    173     v8::Handle<v8::Function> removeBreakpointFunction = v8::Local<v8::Function>::Cast(m_debuggerScript.newLocal(m_isolate)->Get(v8::String::NewSymbol("removeBreakpoint")));
    174     v8::Debug::Call(removeBreakpointFunction, args);
    175 }
    176 
    177 void ScriptDebugServer::clearBreakpoints()
    178 {
    179     ensureDebuggerScriptCompiled();
    180     v8::HandleScope scope(m_isolate);
    181     v8::Local<v8::Context> debuggerContext = v8::Debug::GetDebugContext();
    182     v8::Context::Scope contextScope(debuggerContext);
    183 
    184     v8::Handle<v8::Function> clearBreakpoints = v8::Local<v8::Function>::Cast(m_debuggerScript.newLocal(m_isolate)->Get(v8::String::NewSymbol("clearBreakpoints")));
    185     v8::Debug::Call(clearBreakpoints);
    186 }
    187 
    188 void ScriptDebugServer::setBreakpointsActivated(bool activated)
    189 {
    190     ensureDebuggerScriptCompiled();
    191     v8::HandleScope scope(m_isolate);
    192     v8::Local<v8::Context> debuggerContext = v8::Debug::GetDebugContext();
    193     v8::Context::Scope contextScope(debuggerContext);
    194 
    195     v8::Local<v8::Object> args = v8::Object::New();
    196     args->Set(v8::String::NewSymbol("enabled"), v8::Boolean::New(activated));
    197     v8::Handle<v8::Function> setBreakpointsActivated = v8::Local<v8::Function>::Cast(m_debuggerScript.newLocal(m_isolate)->Get(v8::String::NewSymbol("setBreakpointsActivated")));
    198     v8::Debug::Call(setBreakpointsActivated, args);
    199 
    200     m_breakpointsActivated = activated;
    201 }
    202 
    203 ScriptDebugServer::PauseOnExceptionsState ScriptDebugServer::pauseOnExceptionsState()
    204 {
    205     ensureDebuggerScriptCompiled();
    206     v8::HandleScope scope(m_isolate);
    207     v8::Context::Scope contextScope(v8::Debug::GetDebugContext());
    208 
    209     v8::Handle<v8::Value> argv[] = { v8Undefined() };
    210     v8::Handle<v8::Value> result = callDebuggerMethod("pauseOnExceptionsState", 0, argv);
    211     return static_cast<ScriptDebugServer::PauseOnExceptionsState>(result->Int32Value());
    212 }
    213 
    214 void ScriptDebugServer::setPauseOnExceptionsState(PauseOnExceptionsState pauseOnExceptionsState)
    215 {
    216     ensureDebuggerScriptCompiled();
    217     v8::HandleScope scope(m_isolate);
    218     v8::Context::Scope contextScope(v8::Debug::GetDebugContext());
    219 
    220     v8::Handle<v8::Value> argv[] = { v8::Int32::New(pauseOnExceptionsState) };
    221     callDebuggerMethod("setPauseOnExceptionsState", 1, argv);
    222 }
    223 
    224 void ScriptDebugServer::setPauseOnNextStatement(bool pause)
    225 {
    226     if (isPaused())
    227         return;
    228     if (pause)
    229         v8::Debug::DebugBreak(m_isolate);
    230     else
    231         v8::Debug::CancelDebugBreak(m_isolate);
    232 }
    233 
    234 bool ScriptDebugServer::canBreakProgram()
    235 {
    236     if (!m_breakpointsActivated)
    237         return false;
    238 
    239     // FIXME: Remove this check once m_isolate->GetCurrentContext() does not crash.
    240     if (!v8::Context::InContext())
    241         return false;
    242 
    243     v8::HandleScope scope(m_isolate);
    244     return !m_isolate->GetCurrentContext().IsEmpty();
    245 }
    246 
    247 void ScriptDebugServer::breakProgram()
    248 {
    249     if (!canBreakProgram())
    250         return;
    251 
    252     v8::HandleScope scope(m_isolate);
    253     if (m_breakProgramCallbackTemplate.isEmpty()) {
    254         v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
    255         templ->SetCallHandler(&ScriptDebugServer::breakProgramCallback, v8::External::New(this));
    256         m_breakProgramCallbackTemplate.set(m_isolate, templ);
    257     }
    258 
    259     m_pausedContext = m_isolate->GetCurrentContext();
    260     v8::Handle<v8::Function> breakProgramFunction = m_breakProgramCallbackTemplate.newLocal(m_isolate)->GetFunction();
    261     v8::Debug::Call(breakProgramFunction);
    262     m_pausedContext.Clear();
    263 }
    264 
    265 void ScriptDebugServer::continueProgram()
    266 {
    267     if (isPaused())
    268         quitMessageLoopOnPause();
    269     m_executionState.clear();
    270 }
    271 
    272 void ScriptDebugServer::stepIntoStatement()
    273 {
    274     ASSERT(isPaused());
    275     v8::HandleScope handleScope(m_isolate);
    276     v8::Handle<v8::Value> argv[] = { m_executionState.newLocal(m_isolate) };
    277     callDebuggerMethod(stepIntoV8MethodName, 1, argv);
    278     continueProgram();
    279 }
    280 
    281 void ScriptDebugServer::stepOverStatement()
    282 {
    283     ASSERT(isPaused());
    284     v8::HandleScope handleScope(m_isolate);
    285     v8::Handle<v8::Value> argv[] = { m_executionState.newLocal(m_isolate) };
    286     callDebuggerMethod("stepOverStatement", 1, argv);
    287     continueProgram();
    288 }
    289 
    290 void ScriptDebugServer::stepOutOfFunction()
    291 {
    292     ASSERT(isPaused());
    293     v8::HandleScope handleScope(m_isolate);
    294     v8::Handle<v8::Value> argv[] = { m_executionState.newLocal(m_isolate) };
    295     callDebuggerMethod(stepOutV8MethodName, 1, argv);
    296     continueProgram();
    297 }
    298 
    299 bool ScriptDebugServer::setScriptSource(const String& sourceID, const String& newContent, bool preview, String* error, RefPtr<TypeBuilder::Debugger::SetScriptSourceError>& errorData, ScriptValue* newCallFrames, ScriptObject* result)
    300 {
    301     class EnableLiveEditScope {
    302     public:
    303         EnableLiveEditScope() { v8::Debug::SetLiveEditEnabled(true); }
    304         ~EnableLiveEditScope() { v8::Debug::SetLiveEditEnabled(false); }
    305     };
    306 
    307     ensureDebuggerScriptCompiled();
    308     v8::HandleScope scope(m_isolate);
    309 
    310     OwnPtr<v8::Context::Scope> contextScope;
    311     v8::Handle<v8::Context> debuggerContext = v8::Debug::GetDebugContext();
    312     if (!isPaused())
    313         contextScope = adoptPtr(new v8::Context::Scope(debuggerContext));
    314 
    315     v8::Handle<v8::Value> argv[] = { v8String(sourceID, debuggerContext->GetIsolate()), v8String(newContent, debuggerContext->GetIsolate()), v8Boolean(preview) };
    316 
    317     v8::Local<v8::Value> v8result;
    318     {
    319         EnableLiveEditScope enableLiveEditScope;
    320         v8::TryCatch tryCatch;
    321         tryCatch.SetVerbose(false);
    322         v8result = callDebuggerMethod("liveEditScriptSource", 3, argv);
    323         if (tryCatch.HasCaught()) {
    324             v8::Local<v8::Message> message = tryCatch.Message();
    325             if (!message.IsEmpty())
    326                 *error = toWebCoreStringWithUndefinedOrNullCheck(message->Get());
    327             else
    328                 *error = "Unknown error.";
    329             return false;
    330         }
    331     }
    332     ASSERT(!v8result.IsEmpty());
    333     v8::Local<v8::Object> resultTuple = v8result->ToObject();
    334     int code = static_cast<int>(resultTuple->Get(0)->ToInteger()->Value());
    335     switch (code) {
    336     case 0:
    337         {
    338             v8::Local<v8::Value> normalResult = resultTuple->Get(1);
    339             if (normalResult->IsObject())
    340                 *result = ScriptObject(ScriptState::current(), normalResult->ToObject());
    341             // Call stack may have changed after if the edited function was on the stack.
    342             if (!preview && isPaused())
    343                 *newCallFrames = currentCallFrame();
    344             return true;
    345         }
    346     // Compile error.
    347     case 1:
    348         {
    349             RefPtr<TypeBuilder::Debugger::SetScriptSourceError::CompileError> compileError =
    350                 TypeBuilder::Debugger::SetScriptSourceError::CompileError::create()
    351                     .setMessage(toWebCoreStringWithUndefinedOrNullCheck(resultTuple->Get(2)))
    352                     .setLineNumber(resultTuple->Get(3)->ToInteger()->Value())
    353                     .setColumnNumber(resultTuple->Get(4)->ToInteger()->Value());
    354 
    355             *error = toWebCoreStringWithUndefinedOrNullCheck(resultTuple->Get(1));
    356             errorData = TypeBuilder::Debugger::SetScriptSourceError::create();
    357             errorData->setCompileError(compileError);
    358             return false;
    359         }
    360     }
    361     *error = "Unknown error.";
    362     return false;
    363 }
    364 
    365 
    366 void ScriptDebugServer::updateCallStack(ScriptValue* callFrame)
    367 {
    368     if (isPaused())
    369         *callFrame = currentCallFrame();
    370 }
    371 
    372 
    373 void ScriptDebugServer::setScriptPreprocessor(const String& preprocessorBody)
    374 {
    375     m_scriptPreprocessor.clear();
    376     if (!preprocessorBody.isEmpty())
    377         m_scriptPreprocessor = adoptPtr(new ScriptPreprocessor(preprocessorBody, m_isolate));
    378 }
    379 
    380 PassRefPtr<JavaScriptCallFrame> ScriptDebugServer::wrapCallFrames(v8::Handle<v8::Object> executionState, int maximumLimit)
    381 {
    382     v8::Handle<v8::Value> argv[] = { executionState, v8::Integer::New(maximumLimit) };
    383     v8::Handle<v8::Value> currentCallFrameV8 = callDebuggerMethod("currentCallFrame", 2, argv);
    384 
    385     ASSERT(!currentCallFrameV8.IsEmpty());
    386     if (!currentCallFrameV8->IsObject())
    387         return PassRefPtr<JavaScriptCallFrame>();
    388     return JavaScriptCallFrame::create(v8::Debug::GetDebugContext(), v8::Handle<v8::Object>::Cast(currentCallFrameV8));
    389 }
    390 
    391 ScriptValue ScriptDebugServer::currentCallFrame()
    392 {
    393     ASSERT(isPaused());
    394     v8::HandleScope handleScope(m_isolate);
    395     RefPtr<JavaScriptCallFrame> currentCallFrame = wrapCallFrames(m_executionState.newLocal(m_isolate), -1);
    396     if (!currentCallFrame)
    397         return ScriptValue(v8::Null());
    398     v8::Context::Scope contextScope(m_pausedContext);
    399     return ScriptValue(toV8(currentCallFrame.release(), v8::Handle<v8::Object>(), m_pausedContext->GetIsolate()));
    400 }
    401 
    402 void ScriptDebugServer::interruptAndRun(PassOwnPtr<Task> task, v8::Isolate* isolate)
    403 {
    404     v8::Debug::DebugBreakForCommand(new ClientDataImpl(task), isolate);
    405 }
    406 
    407 void ScriptDebugServer::runPendingTasks()
    408 {
    409     v8::Debug::ProcessDebugMessages();
    410 }
    411 
    412 static ScriptDebugServer* toScriptDebugServer(v8::Handle<v8::Value> data)
    413 {
    414     void* p = v8::Handle<v8::External>::Cast(data)->Value();
    415     return static_cast<ScriptDebugServer*>(p);
    416 }
    417 
    418 void ScriptDebugServer::breakProgramCallback(const v8::FunctionCallbackInfo<v8::Value>& args)
    419 {
    420     ASSERT(2 == args.Length());
    421 
    422     ScriptDebugServer* thisPtr = toScriptDebugServer(args.Data());
    423     v8::Handle<v8::Value> exception;
    424     v8::Handle<v8::Array> hitBreakpoints;
    425     thisPtr->handleProgramBreak(v8::Handle<v8::Object>::Cast(args[0]), exception, hitBreakpoints);
    426 }
    427 
    428 void ScriptDebugServer::handleProgramBreak(v8::Handle<v8::Object> executionState, v8::Handle<v8::Value> exception, v8::Handle<v8::Array> hitBreakpointNumbers)
    429 {
    430     // Don't allow nested breaks.
    431     if (isPaused())
    432         return;
    433 
    434     ScriptDebugListener* listener = getDebugListenerForContext(m_pausedContext);
    435     if (!listener)
    436         return;
    437 
    438     Vector<String> breakpointIds;
    439     if (!hitBreakpointNumbers.IsEmpty()) {
    440         breakpointIds.resize(hitBreakpointNumbers->Length());
    441         for (size_t i = 0; i < hitBreakpointNumbers->Length(); i++)
    442             breakpointIds[i] = toWebCoreStringWithUndefinedOrNullCheck(hitBreakpointNumbers->Get(i));
    443     }
    444 
    445     m_executionState.set(m_isolate, executionState);
    446     ScriptState* currentCallFrameState = ScriptState::forContext(m_pausedContext);
    447     listener->didPause(currentCallFrameState, currentCallFrame(), ScriptValue(exception), breakpointIds);
    448 
    449     m_runningNestedMessageLoop = true;
    450     runMessageLoopOnPause(m_pausedContext);
    451     m_runningNestedMessageLoop = false;
    452 }
    453 
    454 void ScriptDebugServer::handleProgramBreak(const v8::Debug::EventDetails& eventDetails, v8::Handle<v8::Value> exception, v8::Handle<v8::Array> hitBreakpointNumbers)
    455 {
    456     m_pausedContext = eventDetails.GetEventContext();
    457     handleProgramBreak(eventDetails.GetExecutionState(), exception, hitBreakpointNumbers);
    458     m_pausedContext.Clear();
    459 }
    460 
    461 void ScriptDebugServer::v8DebugEventCallback(const v8::Debug::EventDetails& eventDetails)
    462 {
    463     ScriptDebugServer* thisPtr = toScriptDebugServer(eventDetails.GetCallbackData());
    464     thisPtr->handleV8DebugEvent(eventDetails);
    465 }
    466 
    467 bool ScriptDebugServer::executeSkipPauseRequest(ScriptDebugListener::SkipPauseRequest request, v8::Handle<v8::Object> executionState)
    468 {
    469     const char* v8MethodName;
    470     switch (request) {
    471     case ScriptDebugListener::NoSkip:
    472         return false;
    473     case ScriptDebugListener::Continue:
    474         return true;
    475     case ScriptDebugListener::StepInto:
    476         v8MethodName = stepIntoV8MethodName;
    477     case ScriptDebugListener::StepOut:
    478         v8MethodName = stepOutV8MethodName;
    479     }
    480     v8::Handle<v8::Value> argv[] = { executionState };
    481     callDebuggerMethod(stepIntoV8MethodName, 1, argv);
    482     return true;
    483 }
    484 
    485 void ScriptDebugServer::handleV8DebugEvent(const v8::Debug::EventDetails& eventDetails)
    486 {
    487     v8::DebugEvent event = eventDetails.GetEvent();
    488 
    489     if (event == v8::BreakForCommand) {
    490         ClientDataImpl* data = static_cast<ClientDataImpl*>(eventDetails.GetClientData());
    491         data->task()->run();
    492         return;
    493     }
    494 
    495     if (event != v8::Break && event != v8::Exception && event != v8::AfterCompile && event != v8::BeforeCompile)
    496         return;
    497 
    498     v8::Handle<v8::Context> eventContext = eventDetails.GetEventContext();
    499     ASSERT(!eventContext.IsEmpty());
    500 
    501     ScriptDebugListener* listener = getDebugListenerForContext(eventContext);
    502     if (listener) {
    503         v8::HandleScope scope(m_isolate);
    504         v8::Local<v8::Context> debugContext = v8::Debug::GetDebugContext();
    505         v8::Handle<v8::Object> debuggerScript = m_debuggerScript.newLocal(m_isolate);
    506         if (event == v8::BeforeCompile) {
    507 
    508             if (!m_scriptPreprocessor)
    509                 return;
    510 
    511             OwnPtr<ScriptPreprocessor> preprocessor(m_scriptPreprocessor.release());
    512             v8::Context::Scope contextScope(debugContext);
    513             v8::Handle<v8::Function> getScriptSourceFunction = v8::Local<v8::Function>::Cast(debuggerScript->Get(v8::String::New("getScriptSource")));
    514             v8::Handle<v8::Value> argv[] = { eventDetails.GetEventData() };
    515             v8::Handle<v8::Value> script = V8ScriptRunner::callInternalFunction(getScriptSourceFunction, debuggerScript, WTF_ARRAY_LENGTH(argv), argv, m_isolate);
    516 
    517             v8::Handle<v8::Function> getScriptNameFunction = v8::Local<v8::Function>::Cast(debuggerScript->Get(v8::String::New("getScriptName")));
    518             v8::Handle<v8::Value> argv1[] = { eventDetails.GetEventData() };
    519             v8::Handle<v8::Value> scriptName = V8ScriptRunner::callInternalFunction(getScriptNameFunction, debuggerScript, WTF_ARRAY_LENGTH(argv1), argv1, m_isolate);
    520             v8::Handle<v8::Function> setScriptSourceFunction = v8::Local<v8::Function>::Cast(debuggerScript->Get(v8::String::New("setScriptSource")));
    521             String patchedScript = preprocessor->preprocessSourceCode(toWebCoreStringWithUndefinedOrNullCheck(script), toWebCoreStringWithUndefinedOrNullCheck(scriptName));
    522 
    523             v8::Handle<v8::Value> argv2[] = { eventDetails.GetEventData(), v8String(patchedScript, m_isolate) };
    524             V8ScriptRunner::callInternalFunction(setScriptSourceFunction, debuggerScript, WTF_ARRAY_LENGTH(argv2), argv2, m_isolate);
    525             m_scriptPreprocessor = preprocessor.release();
    526         } else if (event == v8::AfterCompile) {
    527             v8::Context::Scope contextScope(v8::Debug::GetDebugContext());
    528             v8::Handle<v8::Function> getAfterCompileScript = v8::Local<v8::Function>::Cast(debuggerScript->Get(v8::String::NewSymbol("getAfterCompileScript")));
    529             v8::Handle<v8::Value> argv[] = { eventDetails.GetEventData() };
    530             v8::Handle<v8::Value> value = V8ScriptRunner::callInternalFunction(getAfterCompileScript, debuggerScript, WTF_ARRAY_LENGTH(argv), argv, m_isolate);
    531             ASSERT(value->IsObject());
    532             v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(value);
    533             dispatchDidParseSource(listener, object);
    534         } else if (event == v8::Exception) {
    535             v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(1);
    536             // Stack trace is empty in case of syntax error. Silently continue execution in such cases.
    537             if (!stackTrace->GetFrameCount())
    538                 return;
    539             RefPtr<JavaScriptCallFrame> topFrame = wrapCallFrames(eventDetails.GetExecutionState(), 1);
    540             if (topFrame && executeSkipPauseRequest(listener->shouldSkipExceptionPause(topFrame), eventDetails.GetExecutionState())) {
    541                 return;
    542             }
    543             v8::Handle<v8::Object> eventData = eventDetails.GetEventData();
    544             v8::Handle<v8::Value> exceptionGetterValue = eventData->Get(v8::String::NewSymbol("exception"));
    545             ASSERT(!exceptionGetterValue.IsEmpty() && exceptionGetterValue->IsFunction());
    546             v8::Handle<v8::Value> exception = V8ScriptRunner::callInternalFunction(v8::Handle<v8::Function>::Cast(exceptionGetterValue), eventData, 0, 0, m_isolate);
    547             handleProgramBreak(eventDetails, exception, v8::Handle<v8::Array>());
    548         } else if (event == v8::Break) {
    549             v8::Handle<v8::Function> getBreakpointNumbersFunction = v8::Local<v8::Function>::Cast(debuggerScript->Get(v8::String::NewSymbol("getBreakpointNumbers")));
    550             v8::Handle<v8::Value> argv[] = { eventDetails.GetEventData() };
    551             v8::Handle<v8::Value> hitBreakpoints = V8ScriptRunner::callInternalFunction(getBreakpointNumbersFunction, debuggerScript, WTF_ARRAY_LENGTH(argv), argv, m_isolate);
    552             ASSERT(hitBreakpoints->IsArray());
    553 
    554             RefPtr<JavaScriptCallFrame> topFrame = wrapCallFrames(eventDetails.GetExecutionState(), 1);
    555             if (topFrame) {
    556                 ScriptDebugListener::SkipPauseRequest skipRequest;
    557                 if (v8::Handle<v8::Array>::Cast(hitBreakpoints)->Length())
    558                     skipRequest = listener->shouldSkipBreakpointPause(topFrame);
    559                 else
    560                     skipRequest = listener->shouldSkipStepPause(topFrame);
    561                 if (executeSkipPauseRequest(skipRequest, eventDetails.GetExecutionState()))
    562                     return;
    563             }
    564 
    565             handleProgramBreak(eventDetails, v8::Handle<v8::Value>(), hitBreakpoints.As<v8::Array>());
    566         }
    567     }
    568 }
    569 
    570 void ScriptDebugServer::dispatchDidParseSource(ScriptDebugListener* listener, v8::Handle<v8::Object> object)
    571 {
    572     String sourceID = toWebCoreStringWithUndefinedOrNullCheck(object->Get(v8::String::NewSymbol("id")));
    573 
    574     ScriptDebugListener::Script script;
    575     script.url = toWebCoreStringWithUndefinedOrNullCheck(object->Get(v8::String::NewSymbol("name")));
    576     script.source = toWebCoreStringWithUndefinedOrNullCheck(object->Get(v8::String::NewSymbol("source")));
    577     script.sourceMappingURL = toWebCoreStringWithUndefinedOrNullCheck(object->Get(v8::String::NewSymbol("sourceMappingURL")));
    578     script.startLine = object->Get(v8::String::NewSymbol("startLine"))->ToInteger()->Value();
    579     script.startColumn = object->Get(v8::String::NewSymbol("startColumn"))->ToInteger()->Value();
    580     script.endLine = object->Get(v8::String::NewSymbol("endLine"))->ToInteger()->Value();
    581     script.endColumn = object->Get(v8::String::NewSymbol("endColumn"))->ToInteger()->Value();
    582     script.isContentScript = object->Get(v8::String::NewSymbol("isContentScript"))->ToBoolean()->Value();
    583 
    584     listener->didParseSource(sourceID, script);
    585 }
    586 
    587 void ScriptDebugServer::ensureDebuggerScriptCompiled()
    588 {
    589     if (!m_debuggerScript.isEmpty())
    590         return;
    591 
    592     v8::HandleScope scope(m_isolate);
    593     v8::Context::Scope contextScope(v8::Debug::GetDebugContext());
    594     v8::Handle<v8::String> source = v8String(String(reinterpret_cast<const char*>(DebuggerScriptSource_js), sizeof(DebuggerScriptSource_js)), m_isolate);
    595     v8::Local<v8::Value> value = V8ScriptRunner::compileAndRunInternalScript(source, m_isolate);
    596     ASSERT(!value.IsEmpty());
    597     ASSERT(value->IsObject());
    598     m_debuggerScript.set(m_isolate, v8::Handle<v8::Object>::Cast(value));
    599 }
    600 
    601 v8::Local<v8::Value> ScriptDebugServer::functionScopes(v8::Handle<v8::Function> function)
    602 {
    603     ensureDebuggerScriptCompiled();
    604 
    605     v8::Handle<v8::Value> argv[] = { function };
    606     return callDebuggerMethod("getFunctionScopes", 1, argv);
    607 }
    608 
    609 v8::Local<v8::Value> ScriptDebugServer::getInternalProperties(v8::Handle<v8::Object>& object)
    610 {
    611     if (m_debuggerScript.isEmpty())
    612         return v8::Local<v8::Value>::New(m_isolate, v8::Undefined());
    613 
    614     v8::Handle<v8::Value> argv[] = { object };
    615     return callDebuggerMethod("getInternalProperties", 1, argv);
    616 }
    617 
    618 v8::Handle<v8::Value> ScriptDebugServer::setFunctionVariableValue(v8::Handle<v8::Value> functionValue, int scopeNumber, const String& variableName, v8::Handle<v8::Value> newValue)
    619 {
    620     v8::Local<v8::Context> debuggerContext = v8::Debug::GetDebugContext();
    621     if (m_debuggerScript.isEmpty())
    622         return v8::ThrowException(v8::String::New("Debugging is not enabled."));
    623 
    624     v8::Handle<v8::Value> argv[] = {
    625         functionValue,
    626         v8::Handle<v8::Value>(v8::Integer::New(scopeNumber)),
    627         v8String(variableName, debuggerContext->GetIsolate()),
    628         newValue
    629     };
    630     return callDebuggerMethod("setFunctionVariableValue", 4, argv);
    631 }
    632 
    633 
    634 bool ScriptDebugServer::isPaused()
    635 {
    636     return !m_executionState.isEmpty();
    637 }
    638 
    639 void ScriptDebugServer::compileScript(ScriptState* state, const String& expression, const String& sourceURL, String* scriptId, String* exceptionMessage)
    640 {
    641     v8::HandleScope handleScope(m_isolate);
    642     v8::Handle<v8::Context> context = state->context();
    643     if (context.IsEmpty())
    644         return;
    645     v8::Context::Scope contextScope(context);
    646 
    647     v8::Handle<v8::String> source = v8String(expression, m_isolate);
    648     v8::TryCatch tryCatch;
    649     v8::Local<v8::Script> script = V8ScriptRunner::compileScript(source, sourceURL, TextPosition(), 0, m_isolate);
    650     if (tryCatch.HasCaught()) {
    651         v8::Local<v8::Message> message = tryCatch.Message();
    652         if (!message.IsEmpty())
    653             *exceptionMessage = toWebCoreStringWithUndefinedOrNullCheck(message->Get());
    654         return;
    655     }
    656     if (script.IsEmpty())
    657         return;
    658 
    659     *scriptId = toWebCoreStringWithUndefinedOrNullCheck(script->Id());
    660     m_compiledScripts.set(*scriptId, adoptPtr(new ScopedPersistent<v8::Script>(script)));
    661 }
    662 
    663 void ScriptDebugServer::clearCompiledScripts()
    664 {
    665     m_compiledScripts.clear();
    666 }
    667 
    668 void ScriptDebugServer::runScript(ScriptState* state, const String& scriptId, ScriptValue* result, bool* wasThrown, String* exceptionMessage)
    669 {
    670     if (!m_compiledScripts.contains(scriptId))
    671         return;
    672     v8::HandleScope handleScope(m_isolate);
    673     ScopedPersistent<v8::Script>* scriptHandle = m_compiledScripts.get(scriptId);
    674     v8::Local<v8::Script> script = scriptHandle->newLocal(m_isolate);
    675     m_compiledScripts.remove(scriptId);
    676     if (script.IsEmpty())
    677         return;
    678 
    679     v8::Handle<v8::Context> context = state->context();
    680     if (context.IsEmpty())
    681         return;
    682     v8::Context::Scope contextScope(context);
    683     v8::TryCatch tryCatch;
    684     v8::Local<v8::Value> value = V8ScriptRunner::runCompiledScript(script, state->scriptExecutionContext());
    685     *wasThrown = false;
    686     if (tryCatch.HasCaught()) {
    687         *wasThrown = true;
    688         *result = ScriptValue(tryCatch.Exception());
    689         v8::Local<v8::Message> message = tryCatch.Message();
    690         if (!message.IsEmpty())
    691             *exceptionMessage = toWebCoreStringWithUndefinedOrNullCheck(message->Get());
    692     } else
    693         *result = ScriptValue(value);
    694 }
    695 
    696 } // namespace WebCore
    697