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