1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "debugger/NoExecute.h"
8 
9 #include "mozilla/Sprintf.h"  // for SprintfLiteral
10 
11 #include <stdio.h>  // for fprintf, stdout
12 
13 #include "debugger/Debugger.h"        // for Debugger
14 #include "js/friend/DumpFunctions.h"  // for DumpBacktrace
15 #include "js/friend/ErrorMessages.h"  // for GetErrorMessage, JSMSG_DEBUGGEE_WOULD_RUN
16 #include "js/Promise.h"               // for AutoDebuggerJobQueueInterruption
17 #include "js/RootingAPI.h"            // for Handle
18 #include "vm/JSContext.h"             // for ProtectedDataContextArg, JSContext
19 #include "vm/JSScript.h"              // for JSScript
20 #include "vm/Realm.h"                 // for AutoRealm, Realm
21 #include "vm/Warnings.h"              // for WarnNumberLatin1
22 
23 #include "vm/Realm-inl.h"  // for AutoRealm::AutoRealm
24 
25 using namespace js;
26 
EnterDebuggeeNoExecute(JSContext * cx,Debugger & dbg,const JS::AutoDebuggerJobQueueInterruption & adjqiProof)27 EnterDebuggeeNoExecute::EnterDebuggeeNoExecute(
28     JSContext* cx, Debugger& dbg,
29     const JS::AutoDebuggerJobQueueInterruption& adjqiProof)
30     : dbg_(dbg), unlocked_(nullptr), reported_(false) {
31   MOZ_ASSERT(adjqiProof.initialized());
32   stack_ = &cx->noExecuteDebuggerTop.ref();
33   prev_ = *stack_;
34   *stack_ = this;
35 }
36 
37 #ifdef DEBUG
38 /* static */
isLockedInStack(JSContext * cx,Debugger & dbg)39 bool EnterDebuggeeNoExecute::isLockedInStack(JSContext* cx, Debugger& dbg) {
40   for (EnterDebuggeeNoExecute* it = cx->noExecuteDebuggerTop; it;
41        it = it->prev_) {
42     if (&it->debugger() == &dbg) {
43       return !it->unlocked_;
44     }
45   }
46   return false;
47 }
48 #endif
49 
50 /* static */
findInStack(JSContext * cx)51 EnterDebuggeeNoExecute* EnterDebuggeeNoExecute::findInStack(JSContext* cx) {
52   Realm* debuggee = cx->realm();
53   for (EnterDebuggeeNoExecute* it = cx->noExecuteDebuggerTop; it;
54        it = it->prev_) {
55     Debugger& dbg = it->debugger();
56     if (!it->unlocked_ && dbg.observesGlobal(debuggee->maybeGlobal())) {
57       return it;
58     }
59   }
60   return nullptr;
61 }
62 
63 /* static */
reportIfFoundInStack(JSContext * cx,HandleScript script)64 bool EnterDebuggeeNoExecute::reportIfFoundInStack(JSContext* cx,
65                                                   HandleScript script) {
66   if (EnterDebuggeeNoExecute* nx = findInStack(cx)) {
67     bool warning = !cx->options().throwOnDebuggeeWouldRun();
68     if (!warning || !nx->reported_) {
69       AutoRealm ar(cx, nx->debugger().toJSObject());
70       nx->reported_ = true;
71       if (cx->options().dumpStackOnDebuggeeWouldRun()) {
72         fprintf(stdout, "Dumping stack for DebuggeeWouldRun:\n");
73         DumpBacktrace(cx);
74       }
75       const char* filename = script->filename() ? script->filename() : "(none)";
76       char linenoStr[15];
77       SprintfLiteral(linenoStr, "%u", script->lineno());
78       // FIXME: filename should be UTF-8 (bug 987069).
79       if (warning) {
80         return WarnNumberLatin1(cx, JSMSG_DEBUGGEE_WOULD_RUN, filename,
81                                 linenoStr);
82       }
83 
84       JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
85                                  JSMSG_DEBUGGEE_WOULD_RUN, filename, linenoStr);
86       return false;
87     }
88   }
89   return true;
90 }
91