Home | History | Annotate | Download | only in parsing
      1 // Copyright 2017 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef V8_PARSING_PREPARSED_SCOPE_DATA_H_
      6 #define V8_PARSING_PREPARSED_SCOPE_DATA_H_
      7 
      8 #include <set>
      9 #include <unordered_map>
     10 #include <vector>
     11 
     12 #include "src/globals.h"
     13 #include "src/handles.h"
     14 #include "src/objects/shared-function-info.h"
     15 #include "src/zone/zone-chunk-list.h"
     16 
     17 namespace v8 {
     18 namespace internal {
     19 
     20 template <typename T>
     21 class Handle;
     22 
     23 class PreParser;
     24 class PreParsedScopeData;
     25 
     26 /*
     27 
     28   Skipping inner functions.
     29 
     30   Consider the following code:
     31   (function eager_outer() {
     32     function lazy_inner() {
     33       let a;
     34       function skip_me() { a; }
     35     }
     36 
     37     return lazy_inner;
     38   })();
     39 
     40   ... lazy_inner(); ...
     41 
     42   When parsing the code the first time, eager_outer is parsed and lazy_inner
     43   (and everything inside it) is preparsed. When lazy_inner is called, we don't
     44   want to parse or preparse skip_me again. Instead, we want to skip over it,
     45   since it has already been preparsed once.
     46 
     47   In order to be able to do this, we need to store the information needed for
     48   allocating the variables in lazy_inner when we preparse it, and then later do
     49   scope allocation based on that data.
     50 
     51   We need the following data for each scope in lazy_inner's scope tree:
     52   For each Variable:
     53   - is_used
     54   - maybe_assigned
     55   - has_forced_context_allocation
     56 
     57   For each Scope:
     58   - inner_scope_calls_eval_.
     59 
     60   ProducedPreParsedScopeData implements storing the above mentioned data and
     61   ConsumedPreParsedScopeData implements restoring it (= setting the context
     62   allocation status of the variables in a Scope (and its subscopes) based on the
     63   data).
     64 
     65  */
     66 
     67 class ProducedPreParsedScopeData : public ZoneObject {
     68  public:
     69   class ByteData : public ZoneObject {
     70    public:
     71     explicit ByteData(Zone* zone)
     72         : backing_store_(zone), free_quarters_in_last_byte_(0) {}
     73 
     74     void WriteUint32(uint32_t data);
     75     void WriteUint8(uint8_t data);
     76     void WriteQuarter(uint8_t data);
     77 
     78 #ifdef DEBUG
     79     // For overwriting previously written data at position 0.
     80     void OverwriteFirstUint32(uint32_t data);
     81 #endif
     82 
     83     Handle<PodArray<uint8_t>> Serialize(Isolate* isolate);
     84 
     85     size_t size() const { return backing_store_.size(); }
     86 
     87    private:
     88     ZoneChunkList<uint8_t> backing_store_;
     89     uint8_t free_quarters_in_last_byte_;
     90   };
     91 
     92   // Create a ProducedPreParsedScopeData object which will collect data as we
     93   // parse.
     94   ProducedPreParsedScopeData(Zone* zone, ProducedPreParsedScopeData* parent);
     95 
     96   // Create a ProducedPreParsedScopeData which is just a proxy for a previous
     97   // produced PreParsedScopeData.
     98   ProducedPreParsedScopeData(Handle<PreParsedScopeData> data, Zone* zone);
     99 
    100   ProducedPreParsedScopeData* parent() const { return parent_; }
    101 
    102   // For gathering the inner function data and splitting it up according to the
    103   // laziness boundaries. Each lazy function gets its own
    104   // ProducedPreParsedScopeData, and so do all lazy functions inside it.
    105   class DataGatheringScope {
    106    public:
    107     DataGatheringScope(DeclarationScope* function_scope, PreParser* preparser);
    108     ~DataGatheringScope();
    109 
    110     void MarkFunctionAsSkippable(int end_position, int num_inner_functions);
    111 
    112    private:
    113     DeclarationScope* function_scope_;
    114     PreParser* preparser_;
    115     ProducedPreParsedScopeData* produced_preparsed_scope_data_;
    116 
    117     DISALLOW_COPY_AND_ASSIGN(DataGatheringScope);
    118   };
    119 
    120   // Saves the information needed for allocating the Scope's (and its
    121   // subscopes') variables.
    122   void SaveScopeAllocationData(DeclarationScope* scope);
    123 
    124   // In some cases, PreParser cannot produce the same Scope structure as
    125   // Parser. If it happens, we're unable to produce the data that would enable
    126   // skipping the inner functions of that function.
    127   void Bailout() {
    128     bailed_out_ = true;
    129 
    130     // We don't need to call Bailout on existing / future children: the only way
    131     // to try to retrieve their data is through calling Serialize on the parent,
    132     // and if the parent is bailed out, it won't call Serialize on its children.
    133   }
    134 
    135   bool bailed_out() const { return bailed_out_; }
    136 
    137 #ifdef DEBUG
    138   bool ThisOrParentBailedOut() const {
    139     if (bailed_out_) {
    140       return true;
    141     }
    142     if (parent_ == nullptr) {
    143       return false;
    144     }
    145     return parent_->ThisOrParentBailedOut();
    146   }
    147 #endif  // DEBUG
    148 
    149   bool ContainsInnerFunctions() const;
    150 
    151   // If there is data (if the Scope contains skippable inner functions), move
    152   // the data into the heap and return a Handle to it; otherwise return a null
    153   // MaybeHandle.
    154   MaybeHandle<PreParsedScopeData> Serialize(Isolate* isolate);
    155 
    156   static bool ScopeNeedsData(Scope* scope);
    157   static bool ScopeIsSkippableFunctionScope(Scope* scope);
    158 
    159  private:
    160   void AddSkippableFunction(int start_position, int end_position,
    161                             int num_parameters, int num_inner_functions,
    162                             LanguageMode language_mode,
    163                             bool uses_super_property);
    164 
    165   void SaveDataForScope(Scope* scope);
    166   void SaveDataForVariable(Variable* var);
    167   void SaveDataForInnerScopes(Scope* scope);
    168 
    169   ProducedPreParsedScopeData* parent_;
    170 
    171   ByteData* byte_data_;
    172   ZoneChunkList<ProducedPreParsedScopeData*> data_for_inner_functions_;
    173 
    174   // Whether we've given up producing the data for this function.
    175   bool bailed_out_;
    176 
    177   // ProducedPreParsedScopeData can also mask a Handle<PreParsedScopeData>
    178   // which was produced already earlier. This happens for deeper lazy functions.
    179   Handle<PreParsedScopeData> previously_produced_preparsed_scope_data_;
    180 
    181   DISALLOW_COPY_AND_ASSIGN(ProducedPreParsedScopeData);
    182 };
    183 
    184 class ConsumedPreParsedScopeData {
    185  public:
    186   class ByteData {
    187    public:
    188     ByteData()
    189         : data_(nullptr), index_(0), stored_quarters_(0), stored_byte_(0) {}
    190 
    191     // Reading from the ByteData is only allowed when a ReadingScope is on the
    192     // stack. This ensures that we have a DisallowHeapAllocation in place
    193     // whenever ByteData holds a raw pointer into the heap.
    194     class ReadingScope {
    195      public:
    196       ReadingScope(ByteData* consumed_data, PodArray<uint8_t>* data)
    197           : consumed_data_(consumed_data) {
    198         consumed_data->data_ = data;
    199       }
    200       explicit ReadingScope(ConsumedPreParsedScopeData* parent);
    201       ~ReadingScope() { consumed_data_->data_ = nullptr; }
    202 
    203      private:
    204       ByteData* consumed_data_;
    205       DisallowHeapAllocation no_gc;
    206     };
    207 
    208     void SetPosition(int position) { index_ = position; }
    209 
    210     int32_t ReadUint32();
    211     uint8_t ReadUint8();
    212     uint8_t ReadQuarter();
    213 
    214     size_t RemainingBytes() const;
    215 
    216     // private:
    217     PodArray<uint8_t>* data_;
    218     int index_;
    219     uint8_t stored_quarters_;
    220     uint8_t stored_byte_;
    221   };
    222 
    223   ConsumedPreParsedScopeData();
    224   ~ConsumedPreParsedScopeData();
    225 
    226   void SetData(Isolate* isolate, Handle<PreParsedScopeData> data);
    227 
    228   bool HasData() const { return !data_.is_null(); }
    229 
    230   ProducedPreParsedScopeData* GetDataForSkippableFunction(
    231       Zone* zone, int start_position, int* end_position, int* num_parameters,
    232       int* num_inner_functions, bool* uses_super_property,
    233       LanguageMode* language_mode);
    234 
    235   // Restores the information needed for allocating the Scope's (and its
    236   // subscopes') variables.
    237   void RestoreScopeAllocationData(DeclarationScope* scope);
    238 
    239  private:
    240   void RestoreData(Scope* scope);
    241   void RestoreDataForVariable(Variable* var);
    242   void RestoreDataForInnerScopes(Scope* scope);
    243 
    244   Isolate* isolate_;
    245   Handle<PreParsedScopeData> data_;
    246   std::unique_ptr<ByteData> scope_data_;
    247   // When consuming the data, these indexes point to the data we're going to
    248   // consume next.
    249   int child_index_;
    250 
    251   DISALLOW_COPY_AND_ASSIGN(ConsumedPreParsedScopeData);
    252 };
    253 
    254 }  // namespace internal
    255 }  // namespace v8
    256 
    257 #endif  // V8_PARSING_PREPARSED_SCOPE_DATA_H_
    258