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 "jsapi.h"        // for Handle
14 #include "jsfriendapi.h"  // for DumpBacktrace, GetErrorMessage
15 
16 #include "debugger/Debugger.h"  // for Debugger
17 #include "gc/Barrier.h"         // for GCPtrNativeObject
18 #include "js/Promise.h"         // for AutoDebuggerJobQueueInterruption
19 #include "vm/JSContext.h"       // for ProtectedDataContextArg, JSContext
20 #include "vm/JSScript.h"        // for JSScript
21 #include "vm/Realm.h"           // for AutoRealm, Realm
22 #include "vm/Warnings.h"        // for WarnNumberLatin1
23 
24 #include "vm/Realm-inl.h"  // for AutoRealm::AutoRealm
25 
26 using namespace js;
27 
EnterDebuggeeNoExecute(JSContext * cx,Debugger & dbg,const JS::AutoDebuggerJobQueueInterruption & adjqiProof)28 EnterDebuggeeNoExecute::EnterDebuggeeNoExecute(
29     JSContext* cx, Debugger& dbg,
30     const JS::AutoDebuggerJobQueueInterruption& adjqiProof)
31     : dbg_(dbg), unlocked_(nullptr), reported_(false) {
32   MOZ_ASSERT(adjqiProof.initialized());
33   stack_ = &cx->noExecuteDebuggerTop.ref();
34   prev_ = *stack_;
35   *stack_ = this;
36 }
37 
38 #ifdef DEBUG
39 /* static */
isLockedInStack(JSContext * cx,Debugger & dbg)40 bool EnterDebuggeeNoExecute::isLockedInStack(JSContext* cx, Debugger& dbg) {
41   for (EnterDebuggeeNoExecute* it = cx->noExecuteDebuggerTop; it;
42        it = it->prev_) {
43     if (&it->debugger() == &dbg) {
44       return !it->unlocked_;
45     }
46   }
47   return false;
48 }
49 #endif
50 
51 /* static */
findInStack(JSContext * cx)52 EnterDebuggeeNoExecute* EnterDebuggeeNoExecute::findInStack(JSContext* cx) {
53   Realm* debuggee = cx->realm();
54   for (EnterDebuggeeNoExecute* it = cx->noExecuteDebuggerTop; it;
55        it = it->prev_) {
56     Debugger& dbg = it->debugger();
57     if (!it->unlocked_ && dbg.observesGlobal(debuggee->maybeGlobal())) {
58       return it;
59     }
60   }
61   return nullptr;
62 }
63 
64 /* static */
reportIfFoundInStack(JSContext * cx,HandleScript script)65 bool EnterDebuggeeNoExecute::reportIfFoundInStack(JSContext* cx,
66                                                   HandleScript script) {
67   if (EnterDebuggeeNoExecute* nx = findInStack(cx)) {
68     bool warning = !cx->options().throwOnDebuggeeWouldRun();
69     if (!warning || !nx->reported_) {
70       AutoRealm ar(cx, nx->debugger().toJSObject());
71       nx->reported_ = true;
72       if (cx->options().dumpStackOnDebuggeeWouldRun()) {
73         fprintf(stdout, "Dumping stack for DebuggeeWouldRun:\n");
74         DumpBacktrace(cx);
75       }
76       const char* filename = script->filename() ? script->filename() : "(none)";
77       char linenoStr[15];
78       SprintfLiteral(linenoStr, "%u", script->lineno());
79       // FIXME: filename should be UTF-8 (bug 987069).
80       if (warning) {
81         return WarnNumberLatin1(cx, JSMSG_DEBUGGEE_WOULD_RUN, filename,
82                                 linenoStr);
83       }
84 
85       JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
86                                  JSMSG_DEBUGGEE_WOULD_RUN, filename, linenoStr);
87       return false;
88     }
89   }
90   return true;
91 }
92