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/. */
7 #ifndef frontend_FrontendContext_h
8 #define frontend_FrontendContext_h
10 #include "mozilla/Assertions.h" // MOZ_ASSERT
11 #include "mozilla/Attributes.h" // MOZ_STACK_CLASS
12 #include "mozilla/Maybe.h" // mozilla::Maybe
14 #include <stddef.h> // size_t
16 #include "js/AllocPolicy.h" // SystemAllocPolicy, AllocFunction
17 #include "js/ErrorReport.h" // JSErrorCallback, JSErrorFormatString
18 #include "js/Stack.h" // JS::NativeStackSize, JS::NativeStackLimit, JS::NativeStackLimitMax
19 #include "js/Vector.h" // Vector
20 #include "vm/ErrorReporting.h" // CompileError
21 #include "vm/MallocProvider.h" // MallocProvider
22 #include "vm/SharedScriptDataTableHolder.h" // js::SharedScriptDataTableHolder, js::globalSharedScriptDataTableHolder
28 class FrontendContext
;
31 class NameCollectionPool
;
32 } // namespace frontend
34 struct FrontendErrors
{
35 FrontendErrors() = default;
36 // Any errors or warnings produced during compilation. These are reported
37 // when finishing the script.
38 mozilla::Maybe
<CompileError
> error
;
39 Vector
<CompileError
, 0, SystemAllocPolicy
> warnings
;
40 bool overRecursed
= false;
41 bool outOfMemory
= false;
42 bool allocationOverflow
= false;
44 // Set to true if the compilation is initiated with extra bindings, but
45 // the script has no reference to the bindings, and the script should be
46 // compiled without the extra bindings.
48 // See frontend::CompileGlobalScriptWithExtraBindings.
49 bool extraBindingsAreNotUsed
= false;
51 bool hadErrors() const {
52 return outOfMemory
|| overRecursed
|| allocationOverflow
||
53 extraBindingsAreNotUsed
|| error
;
60 class FrontendAllocator
: public MallocProvider
<FrontendAllocator
> {
62 FrontendContext
* const fc_
;
65 explicit FrontendAllocator(FrontendContext
* fc
) : fc_(fc
) {}
67 void* onOutOfMemory(js::AllocFunction allocFunc
, arena_id_t arena
,
68 size_t nbytes
, void* reallocPtr
= nullptr);
69 void reportAllocationOverflow();
72 class FrontendContext
{
74 FrontendAllocator alloc_
;
75 js::FrontendErrors errors_
;
77 // NameCollectionPool can be either:
78 // * owned by this FrontendContext, or
79 // * borrowed from JSContext
80 frontend::NameCollectionPool
* nameCollectionPool_
;
81 bool ownNameCollectionPool_
;
83 js::SharedScriptDataTableHolder
* scriptDataTableHolder_
;
85 // Limit pointer for checking native stack consumption.
87 // The pointer is calculated based on the stack base of the current thread
88 // except for JS::NativeStackLimitMax. Once such value is set, this
89 // FrontendContext can be used only in the thread.
91 // In order to enforce this thread rule, setNativeStackLimitThread should
92 // be called when setting the value, and assertNativeStackLimitThread should
93 // be called at each entry-point that might make use of this field.
94 JS::NativeStackLimit stackLimit_
= JS::NativeStackLimitMax
;
97 // The thread ID where the native stack limit is set.
98 mozilla::Maybe
<size_t> stackLimitThreadId_
;
100 // The stack pointer where the AutoCheckRecursionLimit check is performed
102 void* previousStackPointer_
= nullptr;
106 // (optional) Current JSContext to support main-thread-specific
107 // handling for error reporting, GC, and memory allocation.
109 // Set by setCurrentJSContext.
110 JSContext
* maybeCx_
= nullptr;
115 nameCollectionPool_(nullptr),
116 ownNameCollectionPool_(false),
117 scriptDataTableHolder_(&js::globalSharedScriptDataTableHolder
) {}
120 void setStackQuota(JS::NativeStackSize stackSize
);
121 JS::NativeStackLimit
stackLimit() const { return stackLimit_
; }
123 bool allocateOwnedPool();
125 frontend::NameCollectionPool
& nameCollectionPool() {
128 "Either allocateOwnedPool or setCurrentJSContext must be called");
129 return *nameCollectionPool_
;
132 js::SharedScriptDataTableHolder
* scriptDataTableHolder() {
133 MOZ_ASSERT(scriptDataTableHolder_
);
134 return scriptDataTableHolder_
;
137 FrontendAllocator
* getAllocator() { return &alloc_
; }
139 // Use the given JSContext's for:
140 // * js::frontend::NameCollectionPool for reusing allocation
141 // * js::SharedScriptDataTableHolder for de-duplicating bytecode
142 // within given runtime
143 // * Copy the native stack limit from the JSContext
145 // And also this JSContext can be retrieved by maybeCurrentJSContext below.
146 void setCurrentJSContext(JSContext
* cx
);
148 // Returns JSContext if any.
150 // This can be used only for:
151 // * Main-thread-specific operation, such as operating on JSAtom
152 // * Optional operation, such as providing better error message
153 JSContext
* maybeCurrentJSContext() { return maybeCx_
; }
155 enum class Warning
{ Suppress
, Report
};
157 // Returns false if the error cannot be converted (such as due to OOM). An
158 // error might still be reported to the given JSContext. Returns true
160 bool convertToRuntimeError(JSContext
* cx
, Warning warning
= Warning::Report
);
162 mozilla::Maybe
<CompileError
>& maybeError() { return errors_
.error
; }
163 Vector
<CompileError
, 0, SystemAllocPolicy
>& warnings() {
164 return errors_
.warnings
;
167 // Report CompileErrors
168 void reportError(js::CompileError
&& err
);
169 bool reportWarning(js::CompileError
&& err
);
171 // Report FrontendAllocator errors
172 void* onOutOfMemory(js::AllocFunction allocFunc
, arena_id_t arena
,
173 size_t nbytes
, void* reallocPtr
= nullptr);
174 void onAllocationOverflow();
176 void onOutOfMemory();
177 void onOverRecursed();
179 void recoverFromOutOfMemory();
181 const JSErrorFormatString
* gcSafeCallback(JSErrorCallback callback
,
183 const unsigned errorNumber
);
185 // Status of errors reported to this FrontendContext
186 bool hadOutOfMemory() const { return errors_
.outOfMemory
; }
187 bool hadOverRecursed() const { return errors_
.overRecursed
; }
188 bool hadAllocationOverflow() const { return errors_
.allocationOverflow
; }
189 bool extraBindingsAreNotUsed() const {
190 return errors_
.extraBindingsAreNotUsed
;
192 void reportExtraBindingsAreNotUsed() {
193 errors_
.extraBindingsAreNotUsed
= true;
195 void clearNoExtraBindingReferencesFound() {
196 errors_
.extraBindingsAreNotUsed
= false;
198 bool hadErrors() const;
199 // Clear errors and warnings.
201 // Clear warnings only.
202 void clearWarnings();
205 void incWasiRecursionDepth();
206 void decWasiRecursionDepth();
207 bool checkWasiRecursionLimit();
211 void setNativeStackLimitThread();
212 void assertNativeStackLimitThread();
216 void checkAndUpdateFrontendContextRecursionLimit(void* sp
);
220 void ReportOutOfMemory();
221 void addPendingOutOfMemory();
224 // Automatically report any pending exception when leaving the scope.
225 class MOZ_STACK_CLASS AutoReportFrontendContext
: public FrontendContext
{
226 // The target JSContext to report the errors to.
232 explicit AutoReportFrontendContext(JSContext
* cx
,
233 Warning warning
= Warning::Report
)
234 : cx_(cx
), warning_(warning
) {
235 setCurrentJSContext(cx_
);
236 MOZ_ASSERT(cx_
== maybeCx_
);
239 ~AutoReportFrontendContext() {
241 convertToRuntimeErrorAndClear();
245 void clearAutoReport() { cx_
= nullptr; }
247 bool convertToRuntimeErrorAndClear() {
248 bool result
= convertToRuntimeError(cx_
, warning_
);
255 * Explicitly report any pending exception before leaving the scope.
257 * Before an instance of this class leaves the scope, you must call either
258 * failure() (if there are exceptions to report) or ok() (if there are no
259 * exceptions to report).
261 class ManualReportFrontendContext
: public FrontendContext
{
264 bool handled_
= false;
268 explicit ManualReportFrontendContext(JSContext
* cx
) : cx_(cx
) {
269 setCurrentJSContext(cx_
);
272 ~ManualReportFrontendContext() { MOZ_ASSERT(handled_
); }
284 convertToRuntimeError(cx_
);
288 // Create function for FrontendContext, which is manually allocated and
289 // exclusively owned.
290 extern FrontendContext
* NewFrontendContext();
292 // Destroy function for FrontendContext, which was allocated with
293 // NewFrontendContext.
294 extern void DestroyFrontendContext(FrontendContext
* fc
);
298 #endif /* frontend_FrontendContext_h */