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/core/v8/ScriptDebugServer.h"
33
34#include "bindings/core/v8/ScopedPersistent.h"
35#include "bindings/core/v8/ScriptCallStackFactory.h"
36#include "bindings/core/v8/ScriptController.h"
37#include "bindings/core/v8/ScriptSourceCode.h"
38#include "bindings/core/v8/ScriptValue.h"
39#include "bindings/core/v8/V8Binding.h"
40#include "bindings/core/v8/V8JavaScriptCallFrame.h"
41#include "bindings/core/v8/V8ScriptRunner.h"
42#include "core/inspector/JavaScriptCallFrame.h"
43#include "core/inspector/ScriptDebugListener.h"
44#include "platform/JSONValues.h"
45#include "public/platform/Platform.h"
46#include "public/platform/WebData.h"
47#include "wtf/StdLibExtras.h"
48#include "wtf/Vector.h"
49#include "wtf/dtoa/utils.h"
50#include "wtf/text/CString.h"
51
52namespace blink {
53
54namespace {
55
56class ClientDataImpl : public v8::Debug::ClientData {
57public:
58    ClientDataImpl(PassOwnPtr<ScriptDebugServer::Task> task) : m_task(task) { }
59    virtual ~ClientDataImpl() { }
60    ScriptDebugServer::Task* task() const { return m_task.get(); }
61private:
62    OwnPtr<ScriptDebugServer::Task> m_task;
63};
64
65const char stepIntoV8MethodName[] = "stepIntoStatement";
66const char stepOutV8MethodName[] = "stepOutOfFunction";
67}
68
69v8::Local<v8::Value> ScriptDebugServer::callDebuggerMethod(const char* functionName, int argc, v8::Handle<v8::Value> argv[])
70{
71    v8::Handle<v8::Object> debuggerScript = m_debuggerScript.newLocal(m_isolate);
72    v8::Handle<v8::Function> function = v8::Local<v8::Function>::Cast(debuggerScript->Get(v8AtomicString(m_isolate, functionName)));
73    ASSERT(m_isolate->InContext());
74    return V8ScriptRunner::callInternalFunction(function, debuggerScript, argc, argv, m_isolate);
75}
76
77ScriptDebugServer::ScriptDebugServer(v8::Isolate* isolate)
78    : m_pauseOnExceptionsState(DontPauseOnExceptions)
79    , m_breakpointsActivated(true)
80    , m_isolate(isolate)
81    , m_runningNestedMessageLoop(false)
82{
83}
84
85ScriptDebugServer::~ScriptDebugServer()
86{
87}
88
89String ScriptDebugServer::setBreakpoint(const String& sourceID, const ScriptBreakpoint& scriptBreakpoint, int* actualLineNumber, int* actualColumnNumber, bool interstatementLocation)
90{
91    v8::HandleScope scope(m_isolate);
92    v8::Local<v8::Context> debuggerContext = v8::Debug::GetDebugContext();
93    v8::Context::Scope contextScope(debuggerContext);
94
95    v8::Local<v8::Object> info = v8::Object::New(m_isolate);
96    info->Set(v8AtomicString(m_isolate, "sourceID"), v8String(debuggerContext->GetIsolate(), sourceID));
97    info->Set(v8AtomicString(m_isolate, "lineNumber"), v8::Integer::New(debuggerContext->GetIsolate(), scriptBreakpoint.lineNumber));
98    info->Set(v8AtomicString(m_isolate, "columnNumber"), v8::Integer::New(debuggerContext->GetIsolate(), scriptBreakpoint.columnNumber));
99    info->Set(v8AtomicString(m_isolate, "interstatementLocation"), v8Boolean(interstatementLocation, debuggerContext->GetIsolate()));
100    info->Set(v8AtomicString(m_isolate, "condition"), v8String(debuggerContext->GetIsolate(), scriptBreakpoint.condition));
101
102    v8::Handle<v8::Function> setBreakpointFunction = v8::Local<v8::Function>::Cast(m_debuggerScript.newLocal(m_isolate)->Get(v8AtomicString(m_isolate, "setBreakpoint")));
103    v8::Handle<v8::Value> breakpointId = v8::Debug::Call(setBreakpointFunction, info);
104    if (breakpointId.IsEmpty() || !breakpointId->IsString())
105        return "";
106    *actualLineNumber = info->Get(v8AtomicString(m_isolate, "lineNumber"))->Int32Value();
107    *actualColumnNumber = info->Get(v8AtomicString(m_isolate, "columnNumber"))->Int32Value();
108    return toCoreString(breakpointId.As<v8::String>());
109}
110
111void ScriptDebugServer::removeBreakpoint(const String& breakpointId)
112{
113    v8::HandleScope scope(m_isolate);
114    v8::Local<v8::Context> debuggerContext = v8::Debug::GetDebugContext();
115    v8::Context::Scope contextScope(debuggerContext);
116
117    v8::Local<v8::Object> info = v8::Object::New(m_isolate);
118    info->Set(v8AtomicString(m_isolate, "breakpointId"), v8String(debuggerContext->GetIsolate(), breakpointId));
119
120    v8::Handle<v8::Function> removeBreakpointFunction = v8::Local<v8::Function>::Cast(m_debuggerScript.newLocal(m_isolate)->Get(v8AtomicString(m_isolate, "removeBreakpoint")));
121    v8::Debug::Call(removeBreakpointFunction, info);
122}
123
124void ScriptDebugServer::clearBreakpoints()
125{
126    ensureDebuggerScriptCompiled();
127    v8::HandleScope scope(m_isolate);
128    v8::Local<v8::Context> debuggerContext = v8::Debug::GetDebugContext();
129    v8::Context::Scope contextScope(debuggerContext);
130
131    v8::Handle<v8::Function> clearBreakpoints = v8::Local<v8::Function>::Cast(m_debuggerScript.newLocal(m_isolate)->Get(v8AtomicString(m_isolate, "clearBreakpoints")));
132    v8::Debug::Call(clearBreakpoints);
133}
134
135void ScriptDebugServer::setBreakpointsActivated(bool activated)
136{
137    ensureDebuggerScriptCompiled();
138    v8::HandleScope scope(m_isolate);
139    v8::Local<v8::Context> debuggerContext = v8::Debug::GetDebugContext();
140    v8::Context::Scope contextScope(debuggerContext);
141
142    v8::Local<v8::Object> info = v8::Object::New(m_isolate);
143    info->Set(v8AtomicString(m_isolate, "enabled"), v8::Boolean::New(m_isolate, activated));
144    v8::Handle<v8::Function> setBreakpointsActivated = v8::Local<v8::Function>::Cast(m_debuggerScript.newLocal(m_isolate)->Get(v8AtomicString(m_isolate, "setBreakpointsActivated")));
145    v8::Debug::Call(setBreakpointsActivated, info);
146
147    m_breakpointsActivated = activated;
148}
149
150ScriptDebugServer::PauseOnExceptionsState ScriptDebugServer::pauseOnExceptionsState()
151{
152    ensureDebuggerScriptCompiled();
153    v8::HandleScope scope(m_isolate);
154    v8::Context::Scope contextScope(v8::Debug::GetDebugContext());
155
156    v8::Handle<v8::Value> argv[] = { v8Undefined() };
157    v8::Handle<v8::Value> result = callDebuggerMethod("pauseOnExceptionsState", 0, argv);
158    return static_cast<ScriptDebugServer::PauseOnExceptionsState>(result->Int32Value());
159}
160
161void ScriptDebugServer::setPauseOnExceptionsState(PauseOnExceptionsState pauseOnExceptionsState)
162{
163    ensureDebuggerScriptCompiled();
164    v8::HandleScope scope(m_isolate);
165    v8::Context::Scope contextScope(v8::Debug::GetDebugContext());
166
167    v8::Handle<v8::Value> argv[] = { v8::Int32::New(m_isolate, pauseOnExceptionsState) };
168    callDebuggerMethod("setPauseOnExceptionsState", 1, argv);
169}
170
171void ScriptDebugServer::setPauseOnNextStatement(bool pause)
172{
173    ASSERT(!isPaused());
174    if (pause)
175        v8::Debug::DebugBreak(m_isolate);
176    else
177        v8::Debug::CancelDebugBreak(m_isolate);
178}
179
180bool ScriptDebugServer::pausingOnNextStatement()
181{
182    return v8::Debug::CheckDebugBreak(m_isolate);
183}
184
185bool ScriptDebugServer::canBreakProgram()
186{
187    if (!m_breakpointsActivated)
188        return false;
189    return m_isolate->InContext();
190}
191
192void ScriptDebugServer::breakProgram()
193{
194    if (isPaused()) {
195        ASSERT(!m_runningNestedMessageLoop);
196        v8::Handle<v8::Value> exception;
197        v8::Handle<v8::Array> hitBreakpoints;
198        handleProgramBreak(m_pausedScriptState.get(), m_executionState, exception, hitBreakpoints);
199        return;
200    }
201
202    if (!canBreakProgram())
203        return;
204
205    v8::HandleScope scope(m_isolate);
206    if (m_breakProgramCallbackTemplate.isEmpty()) {
207        v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(m_isolate);
208        templ->SetCallHandler(&ScriptDebugServer::breakProgramCallback, v8::External::New(m_isolate, this));
209        m_breakProgramCallbackTemplate.set(m_isolate, templ);
210    }
211
212    v8::Handle<v8::Function> breakProgramFunction = m_breakProgramCallbackTemplate.newLocal(m_isolate)->GetFunction();
213    v8::Debug::Call(breakProgramFunction);
214}
215
216void ScriptDebugServer::continueProgram()
217{
218    if (isPaused())
219        quitMessageLoopOnPause();
220    m_pausedScriptState.clear();
221    m_executionState.Clear();
222}
223
224void ScriptDebugServer::stepIntoStatement()
225{
226    ASSERT(isPaused());
227    ASSERT(!m_executionState.IsEmpty());
228    v8::HandleScope handleScope(m_isolate);
229    v8::Handle<v8::Value> argv[] = { m_executionState };
230    callDebuggerMethod(stepIntoV8MethodName, 1, argv);
231    continueProgram();
232}
233
234void ScriptDebugServer::stepOverStatement()
235{
236    ASSERT(isPaused());
237    ASSERT(!m_executionState.IsEmpty());
238    v8::HandleScope handleScope(m_isolate);
239    v8::Handle<v8::Value> argv[] = { m_executionState };
240    callDebuggerMethod("stepOverStatement", 1, argv);
241    continueProgram();
242}
243
244void ScriptDebugServer::stepOutOfFunction()
245{
246    ASSERT(isPaused());
247    ASSERT(!m_executionState.IsEmpty());
248    v8::HandleScope handleScope(m_isolate);
249    v8::Handle<v8::Value> argv[] = { m_executionState };
250    callDebuggerMethod(stepOutV8MethodName, 1, argv);
251    continueProgram();
252}
253
254bool ScriptDebugServer::setScriptSource(const String& sourceID, const String& newContent, bool preview, String* error, RefPtr<TypeBuilder::Debugger::SetScriptSourceError>& errorData, ScriptValue* newCallFrames, RefPtr<JSONObject>* result)
255{
256    class EnableLiveEditScope {
257    public:
258        explicit EnableLiveEditScope(v8::Isolate* isolate) : m_isolate(isolate) { v8::Debug::SetLiveEditEnabled(m_isolate, true); }
259        ~EnableLiveEditScope() { v8::Debug::SetLiveEditEnabled(m_isolate, false); }
260    private:
261        v8::Isolate* m_isolate;
262    };
263
264    ensureDebuggerScriptCompiled();
265    v8::HandleScope scope(m_isolate);
266
267    OwnPtr<v8::Context::Scope> contextScope;
268    v8::Handle<v8::Context> debuggerContext = v8::Debug::GetDebugContext();
269    if (!isPaused())
270        contextScope = adoptPtr(new v8::Context::Scope(debuggerContext));
271
272    v8::Handle<v8::Value> argv[] = { v8String(m_isolate, sourceID), v8String(m_isolate, newContent), v8Boolean(preview, m_isolate) };
273
274    v8::Local<v8::Value> v8result;
275    {
276        EnableLiveEditScope enableLiveEditScope(m_isolate);
277        v8::TryCatch tryCatch;
278        tryCatch.SetVerbose(false);
279        v8result = callDebuggerMethod("liveEditScriptSource", 3, argv);
280        if (tryCatch.HasCaught()) {
281            v8::Local<v8::Message> message = tryCatch.Message();
282            if (!message.IsEmpty())
283                *error = toCoreStringWithUndefinedOrNullCheck(message->Get());
284            else
285                *error = "Unknown error.";
286            return false;
287        }
288    }
289    ASSERT(!v8result.IsEmpty());
290    v8::Local<v8::Object> resultTuple = v8result->ToObject();
291    int code = static_cast<int>(resultTuple->Get(0)->ToInteger()->Value());
292    switch (code) {
293    case 0:
294        {
295            v8::Local<v8::Value> normalResult = resultTuple->Get(1);
296            RefPtr<JSONValue> jsonResult = v8ToJSONValue(m_isolate, normalResult, JSONValue::maxDepth);
297            if (jsonResult)
298                *result = jsonResult->asObject();
299            // Call stack may have changed after if the edited function was on the stack.
300            if (!preview && isPaused())
301                *newCallFrames = currentCallFrames();
302            return true;
303        }
304    // Compile error.
305    case 1:
306        {
307            RefPtr<TypeBuilder::Debugger::SetScriptSourceError::CompileError> compileError =
308                TypeBuilder::Debugger::SetScriptSourceError::CompileError::create()
309                    .setMessage(toCoreStringWithUndefinedOrNullCheck(resultTuple->Get(2)))
310                    .setLineNumber(resultTuple->Get(3)->ToInteger()->Value())
311                    .setColumnNumber(resultTuple->Get(4)->ToInteger()->Value());
312
313            *error = toCoreStringWithUndefinedOrNullCheck(resultTuple->Get(1));
314            errorData = TypeBuilder::Debugger::SetScriptSourceError::create();
315            errorData->setCompileError(compileError);
316            return false;
317        }
318    }
319    *error = "Unknown error.";
320    return false;
321}
322
323int ScriptDebugServer::frameCount()
324{
325    ASSERT(isPaused());
326    ASSERT(!m_executionState.IsEmpty());
327    v8::Handle<v8::Value> argv[] = { m_executionState };
328    v8::Handle<v8::Value> result = callDebuggerMethod("frameCount", WTF_ARRAY_LENGTH(argv), argv);
329    if (result->IsInt32())
330        return result->Int32Value();
331    return 0;
332}
333
334PassRefPtrWillBeRawPtr<JavaScriptCallFrame> ScriptDebugServer::toJavaScriptCallFrameUnsafe(const ScriptValue& value)
335{
336    if (value.isEmpty())
337        return nullptr;
338    ASSERT(value.isObject());
339    return V8JavaScriptCallFrame::toImpl(v8::Handle<v8::Object>::Cast(value.v8ValueUnsafe()));
340}
341
342PassRefPtrWillBeRawPtr<JavaScriptCallFrame> ScriptDebugServer::wrapCallFrames(int maximumLimit, ScopeInfoDetails scopeDetails)
343{
344    const int scopeBits = 2;
345    COMPILE_ASSERT(NoScopes < (1 << scopeBits), not_enough_bits_to_encode_ScopeInfoDetails);
346
347    ASSERT(maximumLimit >= 0);
348    int data = (maximumLimit << scopeBits) | scopeDetails;
349    v8::Handle<v8::Value> currentCallFrameV8;
350    if (m_executionState.IsEmpty()) {
351        v8::Handle<v8::Function> currentCallFrameFunction = v8::Local<v8::Function>::Cast(m_debuggerScript.newLocal(m_isolate)->Get(v8AtomicString(m_isolate, "currentCallFrame")));
352        currentCallFrameV8 = v8::Debug::Call(currentCallFrameFunction, v8::Integer::New(m_isolate, data));
353    } else {
354        v8::Handle<v8::Value> argv[] = { m_executionState, v8::Integer::New(m_isolate, data) };
355        currentCallFrameV8 = callDebuggerMethod("currentCallFrame", WTF_ARRAY_LENGTH(argv), argv);
356    }
357    ASSERT(!currentCallFrameV8.IsEmpty());
358    if (!currentCallFrameV8->IsObject())
359        return nullptr;
360    return JavaScriptCallFrame::create(v8::Debug::GetDebugContext(), v8::Handle<v8::Object>::Cast(currentCallFrameV8));
361}
362
363ScriptValue ScriptDebugServer::currentCallFramesInner(ScopeInfoDetails scopeDetails)
364{
365    if (!m_isolate->InContext())
366        return ScriptValue();
367    v8::HandleScope handleScope(m_isolate);
368
369    // Filter out stack traces entirely consisting of V8's internal scripts.
370    v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(m_isolate, 1);
371    if (!stackTrace->GetFrameCount())
372        return ScriptValue();
373
374    RefPtrWillBeRawPtr<JavaScriptCallFrame> currentCallFrame = wrapCallFrames(0, scopeDetails);
375    if (!currentCallFrame)
376        return ScriptValue();
377
378    ScriptState* scriptState = m_pausedScriptState ? m_pausedScriptState.get() : ScriptState::current(m_isolate);
379    ScriptState::Scope scope(scriptState);
380    return ScriptValue(scriptState, toV8(currentCallFrame.release(), scriptState->context()->Global(), m_isolate));
381}
382
383ScriptValue ScriptDebugServer::currentCallFrames()
384{
385    return currentCallFramesInner(AllScopes);
386}
387
388ScriptValue ScriptDebugServer::currentCallFramesForAsyncStack()
389{
390    return currentCallFramesInner(FastAsyncScopes);
391}
392
393PassRefPtrWillBeRawPtr<JavaScriptCallFrame> ScriptDebugServer::callFrameNoScopes(int index)
394{
395    v8::Handle<v8::Value> currentCallFrameV8;
396    if (m_executionState.IsEmpty()) {
397        v8::Handle<v8::Function> currentCallFrameFunction = v8::Local<v8::Function>::Cast(m_debuggerScript.newLocal(m_isolate)->Get(v8AtomicString(m_isolate, "currentCallFrameByIndex")));
398        currentCallFrameV8 = v8::Debug::Call(currentCallFrameFunction, v8::Integer::New(m_isolate, index));
399    } else {
400        v8::Handle<v8::Value> argv[] = { m_executionState, v8::Integer::New(m_isolate, index) };
401        currentCallFrameV8 = callDebuggerMethod("currentCallFrameByIndex", WTF_ARRAY_LENGTH(argv), argv);
402    }
403    ASSERT(!currentCallFrameV8.IsEmpty());
404    if (!currentCallFrameV8->IsObject())
405        return nullptr;
406    return JavaScriptCallFrame::create(v8::Debug::GetDebugContext(), v8::Handle<v8::Object>::Cast(currentCallFrameV8));
407}
408
409void ScriptDebugServer::interruptAndRun(PassOwnPtr<Task> task, v8::Isolate* isolate)
410{
411    v8::Debug::DebugBreakForCommand(isolate, new ClientDataImpl(task));
412}
413
414void ScriptDebugServer::runPendingTasks()
415{
416    v8::Debug::ProcessDebugMessages();
417}
418
419static ScriptDebugServer* toScriptDebugServer(v8::Handle<v8::Value> data)
420{
421    void* p = v8::Handle<v8::External>::Cast(data)->Value();
422    return static_cast<ScriptDebugServer*>(p);
423}
424
425void ScriptDebugServer::breakProgramCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
426{
427    ASSERT(2 == info.Length());
428    ScriptDebugServer* thisPtr = toScriptDebugServer(info.Data());
429    ScriptState* pausedScriptState = ScriptState::current(thisPtr->m_isolate);
430    v8::Handle<v8::Value> exception;
431    v8::Handle<v8::Array> hitBreakpoints;
432    thisPtr->handleProgramBreak(pausedScriptState, v8::Handle<v8::Object>::Cast(info[0]), exception, hitBreakpoints);
433}
434
435void ScriptDebugServer::handleProgramBreak(ScriptState* pausedScriptState, v8::Handle<v8::Object> executionState, v8::Handle<v8::Value> exception, v8::Handle<v8::Array> hitBreakpointNumbers)
436{
437    // Don't allow nested breaks.
438    if (m_runningNestedMessageLoop)
439        return;
440
441    ScriptDebugListener* listener = getDebugListenerForContext(pausedScriptState->context());
442    if (!listener)
443        return;
444
445    Vector<String> breakpointIds;
446    if (!hitBreakpointNumbers.IsEmpty()) {
447        breakpointIds.resize(hitBreakpointNumbers->Length());
448        for (size_t i = 0; i < hitBreakpointNumbers->Length(); i++) {
449            v8::Handle<v8::Value> hitBreakpointNumber = hitBreakpointNumbers->Get(i);
450            ASSERT(!hitBreakpointNumber.IsEmpty() && hitBreakpointNumber->IsInt32());
451            breakpointIds[i] = String::number(hitBreakpointNumber->Int32Value());
452        }
453    }
454
455    m_pausedScriptState = pausedScriptState;
456    m_executionState = executionState;
457    ScriptDebugListener::SkipPauseRequest result = listener->didPause(pausedScriptState, currentCallFrames(), ScriptValue(pausedScriptState, exception), breakpointIds);
458    if (result == ScriptDebugListener::NoSkip) {
459        m_runningNestedMessageLoop = true;
460        runMessageLoopOnPause(pausedScriptState->context());
461        m_runningNestedMessageLoop = false;
462    }
463    m_pausedScriptState.clear();
464    m_executionState.Clear();
465
466    if (result == ScriptDebugListener::StepInto) {
467        v8::Handle<v8::Value> argv[] = { executionState };
468        callDebuggerMethod(stepIntoV8MethodName, 1, argv);
469    } else if (result == ScriptDebugListener::StepOut) {
470        v8::Handle<v8::Value> argv[] = { executionState };
471        callDebuggerMethod(stepOutV8MethodName, 1, argv);
472    }
473}
474
475void ScriptDebugServer::v8DebugEventCallback(const v8::Debug::EventDetails& eventDetails)
476{
477    ScriptDebugServer* thisPtr = toScriptDebugServer(eventDetails.GetCallbackData());
478    thisPtr->handleV8DebugEvent(eventDetails);
479}
480
481static v8::Handle<v8::Value> callInternalGetterFunction(v8::Handle<v8::Object> object, const char* functionName, v8::Isolate* isolate)
482{
483    v8::Handle<v8::Value> getterValue = object->Get(v8AtomicString(isolate, functionName));
484    ASSERT(!getterValue.IsEmpty() && getterValue->IsFunction());
485    return V8ScriptRunner::callInternalFunction(v8::Handle<v8::Function>::Cast(getterValue), object, 0, 0, isolate);
486}
487
488void ScriptDebugServer::handleV8DebugEvent(const v8::Debug::EventDetails& eventDetails)
489{
490    v8::DebugEvent event = eventDetails.GetEvent();
491
492    if (event == v8::BreakForCommand) {
493        ClientDataImpl* data = static_cast<ClientDataImpl*>(eventDetails.GetClientData());
494        data->task()->run();
495        return;
496    }
497
498    if (event != v8::AsyncTaskEvent && event != v8::Break && event != v8::Exception && event != v8::AfterCompile && event != v8::BeforeCompile && event != v8::CompileError && event != v8::PromiseEvent)
499        return;
500
501    v8::Handle<v8::Context> eventContext = eventDetails.GetEventContext();
502    ASSERT(!eventContext.IsEmpty());
503
504    ScriptDebugListener* listener = getDebugListenerForContext(eventContext);
505    if (listener) {
506        v8::HandleScope scope(m_isolate);
507        if (event == v8::BeforeCompile) {
508            preprocessBeforeCompile(eventDetails);
509        } else if (event == v8::AfterCompile || event == v8::CompileError) {
510            v8::Context::Scope contextScope(v8::Debug::GetDebugContext());
511            v8::Handle<v8::Value> argv[] = { eventDetails.GetEventData() };
512            v8::Handle<v8::Value> value = callDebuggerMethod("getAfterCompileScript", 1, argv);
513            ASSERT(value->IsObject());
514            v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(value);
515            dispatchDidParseSource(listener, object, event != v8::AfterCompile ? CompileError : CompileSuccess);
516        } else if (event == v8::Exception) {
517            v8::Handle<v8::Object> eventData = eventDetails.GetEventData();
518            v8::Handle<v8::Value> exception = callInternalGetterFunction(eventData, "exception", m_isolate);
519            handleProgramBreak(ScriptState::from(eventContext), eventDetails.GetExecutionState(), exception, v8::Handle<v8::Array>());
520        } else if (event == v8::Break) {
521            v8::Handle<v8::Value> argv[] = { eventDetails.GetEventData() };
522            v8::Handle<v8::Value> hitBreakpoints = callDebuggerMethod("getBreakpointNumbers", 1, argv);
523            ASSERT(hitBreakpoints->IsArray());
524            handleProgramBreak(ScriptState::from(eventContext), eventDetails.GetExecutionState(), v8::Handle<v8::Value>(), hitBreakpoints.As<v8::Array>());
525        } else if (event == v8::AsyncTaskEvent) {
526            if (listener->v8AsyncTaskEventsEnabled())
527                handleV8AsyncTaskEvent(listener, ScriptState::from(eventContext), eventDetails.GetExecutionState(), eventDetails.GetEventData());
528        } else if (event == v8::PromiseEvent) {
529            if (listener->v8PromiseEventsEnabled())
530                handleV8PromiseEvent(listener, ScriptState::from(eventContext), eventDetails.GetExecutionState(), eventDetails.GetEventData());
531        }
532    }
533}
534
535void ScriptDebugServer::handleV8AsyncTaskEvent(ScriptDebugListener* listener, ScriptState* pausedScriptState, v8::Handle<v8::Object> executionState, v8::Handle<v8::Object> eventData)
536{
537    String type = toCoreStringWithUndefinedOrNullCheck(callInternalGetterFunction(eventData, "type", m_isolate));
538    String name = toCoreStringWithUndefinedOrNullCheck(callInternalGetterFunction(eventData, "name", m_isolate));
539    int id = callInternalGetterFunction(eventData, "id", m_isolate)->ToInteger()->Value();
540
541    m_pausedScriptState = pausedScriptState;
542    m_executionState = executionState;
543    listener->didReceiveV8AsyncTaskEvent(pausedScriptState->executionContext(), type, name, id);
544    m_pausedScriptState.clear();
545    m_executionState.Clear();
546}
547
548void ScriptDebugServer::handleV8PromiseEvent(ScriptDebugListener* listener, ScriptState* pausedScriptState, v8::Handle<v8::Object> executionState, v8::Handle<v8::Object> eventData)
549{
550    v8::Handle<v8::Value> argv[] = { eventData };
551    v8::Handle<v8::Value> value = callDebuggerMethod("getPromiseDetails", 1, argv);
552    ASSERT(value->IsObject());
553    v8::Handle<v8::Object> promiseDetails = v8::Handle<v8::Object>::Cast(value);
554    v8::Handle<v8::Object> promise = promiseDetails->Get(v8AtomicString(m_isolate, "promise"))->ToObject();
555    int status = promiseDetails->Get(v8AtomicString(m_isolate, "status"))->ToInteger()->Value();
556    v8::Handle<v8::Value> parentPromise = promiseDetails->Get(v8AtomicString(m_isolate, "parentPromise"));
557
558    m_pausedScriptState = pausedScriptState;
559    m_executionState = executionState;
560    listener->didReceiveV8PromiseEvent(pausedScriptState, promise, parentPromise, status);
561    m_pausedScriptState.clear();
562    m_executionState.Clear();
563}
564
565void ScriptDebugServer::dispatchDidParseSource(ScriptDebugListener* listener, v8::Handle<v8::Object> object, CompileResult compileResult)
566{
567    v8::Handle<v8::Value> id = object->Get(v8AtomicString(m_isolate, "id"));
568    ASSERT(!id.IsEmpty() && id->IsInt32());
569    String sourceID = String::number(id->Int32Value());
570
571    ScriptDebugListener::Script script;
572    script.url = toCoreStringWithUndefinedOrNullCheck(object->Get(v8AtomicString(m_isolate, "name")));
573    script.sourceURL = toCoreStringWithUndefinedOrNullCheck(object->Get(v8AtomicString(m_isolate, "sourceURL")));
574    script.sourceMappingURL = toCoreStringWithUndefinedOrNullCheck(object->Get(v8AtomicString(m_isolate, "sourceMappingURL")));
575    script.source = toCoreStringWithUndefinedOrNullCheck(object->Get(v8AtomicString(m_isolate, "source")));
576    script.startLine = object->Get(v8AtomicString(m_isolate, "startLine"))->ToInteger()->Value();
577    script.startColumn = object->Get(v8AtomicString(m_isolate, "startColumn"))->ToInteger()->Value();
578    script.endLine = object->Get(v8AtomicString(m_isolate, "endLine"))->ToInteger()->Value();
579    script.endColumn = object->Get(v8AtomicString(m_isolate, "endColumn"))->ToInteger()->Value();
580    script.isContentScript = object->Get(v8AtomicString(m_isolate, "isContentScript"))->ToBoolean()->Value();
581
582    listener->didParseSource(sourceID, script, compileResult);
583}
584
585void ScriptDebugServer::ensureDebuggerScriptCompiled()
586{
587    if (!m_debuggerScript.isEmpty())
588        return;
589
590    v8::HandleScope scope(m_isolate);
591    v8::Context::Scope contextScope(v8::Debug::GetDebugContext());
592    const blink::WebData& debuggerScriptSourceResource = blink::Platform::current()->loadResource("DebuggerScriptSource.js");
593    v8::Handle<v8::String> source = v8String(m_isolate, String(debuggerScriptSourceResource.data(), debuggerScriptSourceResource.size()));
594    v8::Local<v8::Value> value = V8ScriptRunner::compileAndRunInternalScript(source, m_isolate);
595    ASSERT(!value.IsEmpty());
596    ASSERT(value->IsObject());
597    m_debuggerScript.set(m_isolate, v8::Handle<v8::Object>::Cast(value));
598}
599
600void ScriptDebugServer::discardDebuggerScript()
601{
602    ASSERT(!m_debuggerScript.isEmpty());
603    m_debuggerScript.clear();
604}
605
606v8::Local<v8::Value> ScriptDebugServer::functionScopes(v8::Handle<v8::Function> function)
607{
608    ensureDebuggerScriptCompiled();
609
610    v8::Handle<v8::Value> argv[] = { function };
611    return callDebuggerMethod("getFunctionScopes", 1, argv);
612}
613
614v8::Local<v8::Value> ScriptDebugServer::collectionEntries(v8::Handle<v8::Object>& object)
615{
616    ensureDebuggerScriptCompiled();
617
618    v8::Handle<v8::Value> argv[] = { object };
619    return callDebuggerMethod("getCollectionEntries", 1, argv);
620}
621
622v8::Local<v8::Value> ScriptDebugServer::getInternalProperties(v8::Handle<v8::Object>& object)
623{
624    if (m_debuggerScript.isEmpty())
625        return v8::Local<v8::Value>::New(m_isolate, v8::Undefined(m_isolate));
626
627    v8::Handle<v8::Value> argv[] = { object };
628    return callDebuggerMethod("getInternalProperties", 1, argv);
629}
630
631v8::Handle<v8::Value> ScriptDebugServer::setFunctionVariableValue(v8::Handle<v8::Value> functionValue, int scopeNumber, const String& variableName, v8::Handle<v8::Value> newValue)
632{
633    v8::Local<v8::Context> debuggerContext = v8::Debug::GetDebugContext();
634    if (m_debuggerScript.isEmpty())
635        return m_isolate->ThrowException(v8::String::NewFromUtf8(m_isolate, "Debugging is not enabled."));
636
637    v8::Handle<v8::Value> argv[] = {
638        functionValue,
639        v8::Handle<v8::Value>(v8::Integer::New(debuggerContext->GetIsolate(), scopeNumber)),
640        v8String(debuggerContext->GetIsolate(), variableName),
641        newValue
642    };
643    return callDebuggerMethod("setFunctionVariableValue", 4, argv);
644}
645
646
647bool ScriptDebugServer::isPaused()
648{
649    return m_pausedScriptState;
650}
651
652void ScriptDebugServer::compileScript(ScriptState* scriptState, const String& expression, const String& sourceURL, String* scriptId, String* exceptionDetailsText, int* lineNumber, int* columnNumber, RefPtrWillBeRawPtr<ScriptCallStack>* stackTrace)
653{
654    if (scriptState->contextIsValid())
655        return;
656    ScriptState::Scope scope(scriptState);
657
658    v8::Handle<v8::String> source = v8String(m_isolate, expression);
659    v8::TryCatch tryCatch;
660    v8::Local<v8::Script> script = V8ScriptRunner::compileScript(source, sourceURL, TextPosition(), 0, 0, m_isolate);
661    if (tryCatch.HasCaught()) {
662        v8::Local<v8::Message> message = tryCatch.Message();
663        if (!message.IsEmpty()) {
664            *exceptionDetailsText = toCoreStringWithUndefinedOrNullCheck(message->Get());
665            *lineNumber = message->GetLineNumber();
666            *columnNumber = message->GetStartColumn();
667            *stackTrace = createScriptCallStack(message->GetStackTrace(), message->GetStackTrace()->GetFrameCount(), m_isolate);
668        }
669        return;
670    }
671    if (script.IsEmpty())
672        return;
673
674    *scriptId = String::number(script->GetUnboundScript()->GetId());
675    m_compiledScripts.set(*scriptId, adoptPtr(new ScopedPersistent<v8::Script>(m_isolate, script)));
676}
677
678void ScriptDebugServer::clearCompiledScripts()
679{
680    m_compiledScripts.clear();
681}
682
683void ScriptDebugServer::runScript(ScriptState* scriptState, const String& scriptId, ScriptValue* result, bool* wasThrown, String* exceptionDetailsText, int* lineNumber, int* columnNumber, RefPtrWillBeRawPtr<ScriptCallStack>* stackTrace)
684{
685    if (!m_compiledScripts.contains(scriptId))
686        return;
687    v8::HandleScope handleScope(m_isolate);
688    ScopedPersistent<v8::Script>* scriptHandle = m_compiledScripts.get(scriptId);
689    v8::Local<v8::Script> script = scriptHandle->newLocal(m_isolate);
690    m_compiledScripts.remove(scriptId);
691    if (script.IsEmpty())
692        return;
693
694    if (scriptState->contextIsValid())
695        return;
696    ScriptState::Scope scope(scriptState);
697    v8::TryCatch tryCatch;
698    v8::Local<v8::Value> value = V8ScriptRunner::runCompiledScript(script, scriptState->executionContext(), m_isolate);
699    *wasThrown = false;
700    if (tryCatch.HasCaught()) {
701        *wasThrown = true;
702        *result = ScriptValue(scriptState, tryCatch.Exception());
703        v8::Local<v8::Message> message = tryCatch.Message();
704        if (!message.IsEmpty()) {
705            *exceptionDetailsText = toCoreStringWithUndefinedOrNullCheck(message->Get());
706            *lineNumber = message->GetLineNumber();
707            *columnNumber = message->GetStartColumn();
708            *stackTrace = createScriptCallStack(message->GetStackTrace(), message->GetStackTrace()->GetFrameCount(), m_isolate);
709        }
710    } else {
711        *result = ScriptValue(scriptState, value);
712    }
713}
714
715PassOwnPtr<ScriptSourceCode> ScriptDebugServer::preprocess(LocalFrame*, const ScriptSourceCode&)
716{
717    return PassOwnPtr<ScriptSourceCode>();
718}
719
720String ScriptDebugServer::preprocessEventListener(LocalFrame*, const String& source, const String& url, const String& functionName)
721{
722    return source;
723}
724
725} // namespace blink
726