tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

FrontendContext.h (9122B)


      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 #ifndef frontend_FrontendContext_h
      8 #define frontend_FrontendContext_h
      9 
     10 #include "mozilla/Assertions.h"  // MOZ_ASSERT
     11 #include "mozilla/Attributes.h"  // MOZ_STACK_CLASS
     12 #include "mozilla/Maybe.h"       // mozilla::Maybe
     13 
     14 #include <stddef.h>  // size_t
     15 
     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
     23 
     24 struct JSContext;
     25 
     26 namespace js {
     27 
     28 class FrontendContext;
     29 
     30 namespace frontend {
     31 class NameCollectionPool;
     32 }  // namespace frontend
     33 
     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;
     43 
     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.
     47  //
     48  // See frontend::CompileGlobalScriptWithExtraBindings.
     49  bool extraBindingsAreNotUsed = false;
     50 
     51  bool hadErrors() const {
     52    return outOfMemory || overRecursed || allocationOverflow ||
     53           extraBindingsAreNotUsed || error;
     54  }
     55 
     56  void clearErrors();
     57  void clearWarnings();
     58 };
     59 
     60 class FrontendAllocator : public MallocProvider<FrontendAllocator> {
     61 private:
     62  FrontendContext* const fc_;
     63 
     64 public:
     65  explicit FrontendAllocator(FrontendContext* fc) : fc_(fc) {}
     66 
     67  void* onOutOfMemory(js::AllocFunction allocFunc, arena_id_t arena,
     68                      size_t nbytes, void* reallocPtr = nullptr);
     69  void reportAllocationOverflow();
     70 };
     71 
     72 class FrontendContext {
     73 private:
     74  FrontendAllocator alloc_;
     75  js::FrontendErrors errors_;
     76 
     77  // NameCollectionPool can be either:
     78  //   * owned by this FrontendContext, or
     79  //   * borrowed from JSContext
     80  frontend::NameCollectionPool* nameCollectionPool_;
     81  bool ownNameCollectionPool_;
     82 
     83  js::SharedScriptDataTableHolder* scriptDataTableHolder_;
     84 
     85  // Limit pointer for checking native stack consumption.
     86  //
     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.
     90  //
     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;
     95 
     96 #ifdef DEBUG
     97  // The thread ID where the native stack limit is set.
     98  mozilla::Maybe<size_t> stackLimitThreadId_;
     99 
    100  // The stack pointer where the AutoCheckRecursionLimit check is performed
    101  // last time.
    102  void* previousStackPointer_ = nullptr;
    103 #endif
    104 
    105 protected:
    106  // (optional) Current JSContext to support main-thread-specific
    107  // handling for error reporting, GC, and memory allocation.
    108  //
    109  // Set by setCurrentJSContext.
    110  JSContext* maybeCx_ = nullptr;
    111 
    112 public:
    113  FrontendContext()
    114      : alloc_(this),
    115        nameCollectionPool_(nullptr),
    116        ownNameCollectionPool_(false),
    117        scriptDataTableHolder_(&js::globalSharedScriptDataTableHolder) {}
    118  ~FrontendContext();
    119 
    120  void setStackQuota(JS::NativeStackSize stackSize);
    121  JS::NativeStackLimit stackLimit() const { return stackLimit_; }
    122 
    123  bool allocateOwnedPool();
    124 
    125  frontend::NameCollectionPool& nameCollectionPool() {
    126    MOZ_ASSERT(
    127        nameCollectionPool_,
    128        "Either allocateOwnedPool or setCurrentJSContext must be called");
    129    return *nameCollectionPool_;
    130  }
    131 
    132  js::SharedScriptDataTableHolder* scriptDataTableHolder() {
    133    MOZ_ASSERT(scriptDataTableHolder_);
    134    return scriptDataTableHolder_;
    135  }
    136 
    137  FrontendAllocator* getAllocator() { return &alloc_; }
    138 
    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
    144  //
    145  // And also this JSContext can be retrieved by maybeCurrentJSContext below.
    146  void setCurrentJSContext(JSContext* cx);
    147 
    148  // Returns JSContext if any.
    149  //
    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_; }
    154 
    155  enum class Warning { Suppress, Report };
    156 
    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
    159  // otherwise.
    160  bool convertToRuntimeError(JSContext* cx, Warning warning = Warning::Report);
    161 
    162  mozilla::Maybe<CompileError>& maybeError() { return errors_.error; }
    163  Vector<CompileError, 0, SystemAllocPolicy>& warnings() {
    164    return errors_.warnings;
    165  }
    166 
    167  // Report CompileErrors
    168  void reportError(js::CompileError&& err);
    169  bool reportWarning(js::CompileError&& err);
    170 
    171  // Report FrontendAllocator errors
    172  void* onOutOfMemory(js::AllocFunction allocFunc, arena_id_t arena,
    173                      size_t nbytes, void* reallocPtr = nullptr);
    174  void onAllocationOverflow();
    175 
    176  void onOutOfMemory();
    177  void onOverRecursed();
    178 
    179  void recoverFromOutOfMemory();
    180 
    181  const JSErrorFormatString* gcSafeCallback(JSErrorCallback callback,
    182                                            void* userRef,
    183                                            const unsigned errorNumber);
    184 
    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;
    191  }
    192  void reportExtraBindingsAreNotUsed() {
    193    errors_.extraBindingsAreNotUsed = true;
    194  }
    195  void clearNoExtraBindingReferencesFound() {
    196    errors_.extraBindingsAreNotUsed = false;
    197  }
    198  bool hadErrors() const;
    199  // Clear errors and warnings.
    200  void clearErrors();
    201  // Clear warnings only.
    202  void clearWarnings();
    203 
    204 #ifdef __wasi__
    205  void incWasiRecursionDepth();
    206  void decWasiRecursionDepth();
    207  bool checkWasiRecursionLimit();
    208 #endif  // __wasi__
    209 
    210 #ifdef DEBUG
    211  void setNativeStackLimitThread();
    212  void assertNativeStackLimitThread();
    213 #endif
    214 
    215 #ifdef DEBUG
    216  void checkAndUpdateFrontendContextRecursionLimit(void* sp);
    217 #endif
    218 
    219 private:
    220  void ReportOutOfMemory();
    221  void addPendingOutOfMemory();
    222 };
    223 
    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.
    227  JSContext* cx_;
    228 
    229  Warning warning_;
    230 
    231 public:
    232  explicit AutoReportFrontendContext(JSContext* cx,
    233                                     Warning warning = Warning::Report)
    234      : cx_(cx), warning_(warning) {
    235    setCurrentJSContext(cx_);
    236    MOZ_ASSERT(cx_ == maybeCx_);
    237  }
    238 
    239  ~AutoReportFrontendContext() {
    240    if (cx_) {
    241      convertToRuntimeErrorAndClear();
    242    }
    243  }
    244 
    245  void clearAutoReport() { cx_ = nullptr; }
    246 
    247  bool convertToRuntimeErrorAndClear() {
    248    bool result = convertToRuntimeError(cx_, warning_);
    249    cx_ = nullptr;
    250    return result;
    251  }
    252 };
    253 
    254 /*
    255 * Explicitly report any pending exception before leaving the scope.
    256 *
    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).
    260 */
    261 class ManualReportFrontendContext : public FrontendContext {
    262  JSContext* cx_;
    263 #ifdef DEBUG
    264  bool handled_ = false;
    265 #endif
    266 
    267 public:
    268  explicit ManualReportFrontendContext(JSContext* cx) : cx_(cx) {
    269    setCurrentJSContext(cx_);
    270  }
    271 
    272  ~ManualReportFrontendContext() { MOZ_ASSERT(handled_); }
    273 
    274  void ok() {
    275 #ifdef DEBUG
    276    handled_ = true;
    277 #endif
    278  }
    279 
    280  void failure() {
    281 #ifdef DEBUG
    282    handled_ = true;
    283 #endif
    284    convertToRuntimeError(cx_);
    285  }
    286 };
    287 
    288 // Create function for FrontendContext, which is manually allocated and
    289 // exclusively owned.
    290 extern FrontendContext* NewFrontendContext();
    291 
    292 // Destroy function for FrontendContext, which was allocated with
    293 // NewFrontendContext.
    294 extern void DestroyFrontendContext(FrontendContext* fc);
    295 
    296 }  // namespace js
    297 
    298 #endif /* frontend_FrontendContext_h */