tor-browser

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

ScopeBindingCache.h (11945B)


      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_ScopeBindingCache_h
      8 #define frontend_ScopeBindingCache_h
      9 
     10 #include "mozilla/Assertions.h"  // mozilla::MakeCompilerAssumeUnreachableFakeValue
     11 #include "mozilla/Attributes.h"  // MOZ_STACK_CLASS
     12 #include "mozilla/HashTable.h"   // mozilla::HashMap
     13 
     14 #include "jstypes.h"  // JS_PUBLIC_API
     15 
     16 #include "frontend/NameAnalysisTypes.h"  // NameLocation
     17 #include "frontend/ParserAtom.h"  // TaggedParserAtomIndex, ParserAtomsTable
     18 
     19 #include "js/Utility.h"  // AutoEnterOOMUnsafeRegion
     20 
     21 #include "vm/StringType.h"  // JSAtom
     22 
     23 namespace js {
     24 
     25 template <typename NameT>
     26 class AbstractBaseScopeData;
     27 
     28 namespace frontend {
     29 
     30 struct CompilationAtomCache;
     31 struct CompilationStencil;
     32 struct ScopeStencilRef;
     33 struct FakeStencilGlobalScope;
     34 struct CompilationStencilMerger;
     35 
     36 // Generic atom wrapper which provides a way to interpret any Atom given
     37 // contextual information. Thus, this structure offers the ability to compare
     38 // Atom from different domains.
     39 //
     40 // This structure provides a `hash` field which is universal across all Atom
     41 // representations. Thus, Atoms from different contexes can be registered in a
     42 // hash table and looked up with a different Atom kind.
     43 struct GenericAtom {
     44  // Emitter names are TaggedParserAtomIndex which are registered in the
     45  // ParserAtomsTable of an extensible compilation stencil, frequently related
     46  // to bytecode emitter, which lookup names in the scope chain to replace names
     47  // by variable locations.
     48  struct EmitterName {
     49    FrontendContext* fc;
     50    ParserAtomsTable& parserAtoms;
     51    CompilationAtomCache& atomCache;
     52    TaggedParserAtomIndex index;
     53 
     54    EmitterName(FrontendContext* fc, ParserAtomsTable& parserAtoms,
     55                CompilationAtomCache& atomCache, TaggedParserAtomIndex index)
     56        : fc(fc),
     57          parserAtoms(parserAtoms),
     58          atomCache(atomCache),
     59          index(index) {}
     60  };
     61 
     62  // Stencil names are TaggedParserAtomIndex which are registered in a
     63  // ParserAtomVector of a compilation stencil, frequently related to the result
     64  // of a compilation. It can be seen while manipulating names of a scope chain
     65  // while delazifying functions using a stencil for context.
     66  struct StencilName {
     67    const CompilationStencil& stencil;
     68    TaggedParserAtomIndex index;
     69  };
     70 
     71  // Any names are references to different Atom representation, including some
     72  // which are interpretable given some contexts such as EmitterName and
     73  // StencilName.
     74  using AnyName = mozilla::Variant<EmitterName, StencilName, JSAtom*>;
     75 
     76  HashNumber hash;
     77  AnyName ref;
     78 
     79  // Constructor for atoms managed by an ExtensibleCompilationState, while
     80  // compiling a script.
     81  GenericAtom(FrontendContext* fc, ParserAtomsTable& parserAtoms,
     82              CompilationAtomCache& atomCache, TaggedParserAtomIndex index);
     83 
     84  // Constructors for atoms managed by a CompilationStencil or a
     85  // BorrowingCompilationStencil, which provide contextual information from an
     86  // already compiled script.
     87  GenericAtom(const CompilationStencil& context, TaggedParserAtomIndex index);
     88  GenericAtom(ScopeStencilRef& scope, TaggedParserAtomIndex index);
     89  GenericAtom(const FakeStencilGlobalScope& scope, TaggedParserAtomIndex index)
     90      : ref((JSAtom*)nullptr) {
     91    MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE();
     92  }
     93 
     94  // Constructor for atoms managed by the Garbage Collector, while providing
     95  // contextual scope information when delazifying functions on the main thread.
     96  GenericAtom(const Scope*, JSAtom* ptr) : GenericAtom(ptr) {}
     97  explicit GenericAtom(JSAtom* ptr) : ref(ptr) { hash = ptr->hash(); }
     98 
     99  bool operator==(const GenericAtom& other) const;
    100 };
    101 
    102 template <typename NameT>
    103 struct BindingHasher;
    104 
    105 template <>
    106 struct BindingHasher<TaggedParserAtomIndex> {
    107  // This is a GenericAtom::StencilName stripped from its context which is the
    108  // same for every key.
    109  using Key = TaggedParserAtomIndex;
    110  struct Lookup {
    111    // When building a BindingMap, we assume that the TaggedParserAtomIndex is
    112    // coming from an existing Stencil, and is not an EmitterName.
    113    const CompilationStencil& keyStencil;
    114    GenericAtom other;
    115 
    116    Lookup(ScopeStencilRef& scope_ref, const GenericAtom& other);
    117    Lookup(const FakeStencilGlobalScope& scope_ref, const GenericAtom& other)
    118        : keyStencil(mozilla::MakeCompilerAssumeUnreachableFakeValue<
    119                     const CompilationStencil&>()),
    120          other(other) {
    121      MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE();
    122    }
    123  };
    124 
    125  static HashNumber hash(const Lookup& aLookup) { return aLookup.other.hash; }
    126 
    127  static bool match(const Key& aKey, const Lookup& aLookup) {
    128    GenericAtom key(aLookup.keyStencil, aKey);
    129    return key == aLookup.other;
    130  }
    131 };
    132 
    133 template <>
    134 struct BindingHasher<JSAtom*> {
    135  using Key = JSAtom*;
    136  struct Lookup {
    137    GenericAtom other;
    138 
    139    template <typename Any>
    140    Lookup(const Any&, const GenericAtom& other) : other(other) {}
    141  };
    142 
    143  static HashNumber hash(const Lookup& aLookup) { return aLookup.other.hash; }
    144 
    145  static bool match(const Key& aKey, const Lookup& aLookup) {
    146    GenericAtom key(aKey);
    147    return key == aLookup.other;
    148  }
    149 };
    150 
    151 // Map the bound names to their respective name location. This is used to avoid
    152 // doing a linear lookup over the list of bindings each time we are looking for
    153 // a single name.
    154 //
    155 // The names given used as a key are either JSAtom in the case of a on-demand
    156 // delazification, or a TaggedParserAtomIndex in case of a
    157 // concurrent-delazification. In both case Lookup arguments are not trivially
    158 // created out of a key, as in the case of a TaggedParserAtomIndex, the
    159 // CompilationStencil should be provided to interpret the TaggedParserAtomIndex
    160 // which are stored in this hash table.
    161 template <typename NameT>
    162 struct BindingMap {
    163  using Lookup = typename BindingHasher<NameT>::Lookup;
    164  using Map =
    165      HashMap<NameT, NameLocation, BindingHasher<NameT>, js::SystemAllocPolicy>;
    166 
    167  Map hashMap;
    168  mozilla::Maybe<NameLocation> catchAll;
    169 };
    170 
    171 // For each list of bound names, map the list of bound names to the hash table
    172 // which is used to reduce the time needed per lookup.
    173 //
    174 // The name parameter are either JSAtom in the case of a on-demand
    175 // delazification, or a TaggedParserAtomIndex in case of a
    176 // concurrent-delazification.
    177 template <typename NameT, typename ScopeT = NameT>
    178 using ScopeBindingMap =
    179    HashMap<AbstractBaseScopeData<ScopeT>*, BindingMap<NameT>,
    180            DefaultHasher<AbstractBaseScopeData<ScopeT>*>,
    181            js::SystemAllocPolicy>;
    182 
    183 // Common interface for a cache holding the mapping of Scope to a hash table
    184 // which mirror the binding mapping stored in the scope.
    185 class ScopeBindingCache {
    186 public:
    187  using CacheGeneration = size_t;
    188 
    189  virtual CacheGeneration getCurrentGeneration() const = 0;
    190 
    191  // Check whether the cache provided as argument is capable of storing the type
    192  // of scope given as arguments.
    193  virtual bool canCacheFor(Scope* ptr);
    194  virtual bool canCacheFor(ScopeStencilRef ref);
    195  virtual bool canCacheFor(const FakeStencilGlobalScope& ref);
    196 
    197  // Create a new BindingMap cache for a given scope. This cache should then be
    198  // filled with all names which might be looked up.
    199  virtual BindingMap<JSAtom*>* createCacheFor(Scope* ptr);
    200  virtual BindingMap<TaggedParserAtomIndex>* createCacheFor(
    201      ScopeStencilRef ref);
    202  virtual BindingMap<TaggedParserAtomIndex>* createCacheFor(
    203      const FakeStencilGlobalScope& ref);
    204 
    205  // Return the BindingMap created for the associated scope, unless the
    206  // generation value does not match the one stored internally, in which case a
    207  // null pointer is always returned.
    208  virtual BindingMap<JSAtom*>* lookupScope(Scope* ptr, CacheGeneration gen);
    209  virtual BindingMap<TaggedParserAtomIndex>* lookupScope(ScopeStencilRef ref,
    210                                                         CacheGeneration gen);
    211  virtual BindingMap<TaggedParserAtomIndex>* lookupScope(
    212      const FakeStencilGlobalScope& ref, CacheGeneration gen);
    213 };
    214 
    215 // NoScopeBindingCache is a no-op which does not implement a ScopeBindingCache.
    216 //
    217 // This is useful when compiling a global script or module, where we are not
    218 // interested in looking up anything from the enclosing scope chain.
    219 class NoScopeBindingCache final : public ScopeBindingCache {
    220 public:
    221  CacheGeneration getCurrentGeneration() const override { return 1; };
    222 
    223  bool canCacheFor(Scope* ptr) override;
    224  bool canCacheFor(ScopeStencilRef ref) override;
    225  bool canCacheFor(const FakeStencilGlobalScope& ref) override;
    226 };
    227 
    228 // StencilScopeBindingCache provides an interface to cache the bindings provided
    229 // by a CompilationStencilMerger.
    230 //
    231 // This cache lives on the stack and its content would be invalidated once going
    232 // out of scope. The constructor expects a reference to a
    233 // CompilationStencilMerger, that is expected to:
    234 //   - out-live this class.
    235 //   - contain the enclosing scope which are manipulated by this class.
    236 //   - be the receiver of delazified functions.
    237 class MOZ_STACK_CLASS StencilScopeBindingCache final
    238    : public ScopeBindingCache {
    239  ScopeBindingMap<TaggedParserAtomIndex> scopeMap;
    240 #ifdef DEBUG
    241  const InitialStencilAndDelazifications& stencils_;
    242 #endif
    243 
    244 public:
    245  explicit StencilScopeBindingCache(
    246      const InitialStencilAndDelazifications& stencils)
    247 #ifdef DEBUG
    248      : stencils_(stencils)
    249 #endif
    250  {
    251  }
    252 
    253  // The cache content is always valid as long as it does not out-live the
    254  // CompilationStencilMerger. No need for a generation number.
    255  CacheGeneration getCurrentGeneration() const override { return 1; }
    256 
    257  bool canCacheFor(ScopeStencilRef ref) override;
    258  bool canCacheFor(const FakeStencilGlobalScope& ref) override;
    259 
    260  BindingMap<TaggedParserAtomIndex>* createCacheFor(
    261      ScopeStencilRef ref) override;
    262  BindingMap<TaggedParserAtomIndex>* createCacheFor(
    263      const FakeStencilGlobalScope& ref) override;
    264 
    265  BindingMap<TaggedParserAtomIndex>* lookupScope(ScopeStencilRef ref,
    266                                                 CacheGeneration gen) override;
    267  BindingMap<TaggedParserAtomIndex>* lookupScope(
    268      const FakeStencilGlobalScope& ref, CacheGeneration gen) override;
    269 };
    270 
    271 // RuntimeScopeBindingCache is used to hold the binding map for each scope which
    272 // is hold by a Scope managed by the garbage collector.
    273 //
    274 // This cache is not thread safe.
    275 //
    276 // The generation number is used to assert the validity of the cached content.
    277 // During a GC, the cached content is thrown away and getCurrentGeneration
    278 // returns a different number. When the generation number differs from the
    279 // initialization of the cached content, the cache content might be renewed or
    280 // ignored.
    281 class RuntimeScopeBindingCache final : public ScopeBindingCache {
    282  ScopeBindingMap<JSAtom*, JSAtom> scopeMap;
    283 
    284  // This value is initialized to 1, such that we can differentiate it from the
    285  // typical 0-init of size_t values, when non-initialized.
    286  size_t cacheGeneration = 1;
    287 
    288 public:
    289  CacheGeneration getCurrentGeneration() const override {
    290    return cacheGeneration;
    291  }
    292 
    293  bool canCacheFor(Scope* ptr) override;
    294  BindingMap<JSAtom*>* createCacheFor(Scope* ptr) override;
    295  BindingMap<JSAtom*>* lookupScope(Scope* ptr, CacheGeneration gen) override;
    296 
    297  // The ScopeBindingCache is not instrumented for tracing weakly the keys used
    298  // for mapping to the NameLocation. Instead, we always purge during compaction
    299  // or collection, and increment the cacheGeneration to notify all consumers
    300  // that the cache can no longer be used without being re-populated.
    301  void purge() {
    302    cacheGeneration++;
    303    scopeMap.clearAndCompact();
    304  }
    305 };
    306 
    307 }  // namespace frontend
    308 }  // namespace js
    309 
    310 #endif  // frontend_ScopeBindingCache_h