preparse-data.h 10.1 KB
Newer Older
1 2 3 4
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
#ifndef V8_PARSING_PREPARSE_DATA_H_
#define V8_PARSING_PREPARSE_DATA_H_
7

8 9
#include <memory>

10
#include "src/common/globals.h"
11 12
#include "src/handles/handles.h"
#include "src/handles/maybe-handles.h"
13
#include "src/utils/scoped-list.h"
14
#include "src/utils/vector.h"
15
#include "src/zone/zone-chunk-list.h"
16
#include "src/zone/zone-containers.h"
17 18 19 20

namespace v8 {
namespace internal {

21
template <typename T>
22
class PodArray;
23

24
class Parser;
25
class PreParser;
26 27
class PreparseData;
class ZonePreparseData;
28
class AstValueFactory;
29

30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
/*

  Skipping inner functions.

  Consider the following code:
  (function eager_outer() {
    function lazy_inner() {
      let a;
      function skip_me() { a; }
    }

    return lazy_inner;
  })();

  ... lazy_inner(); ...

  When parsing the code the first time, eager_outer is parsed and lazy_inner
  (and everything inside it) is preparsed. When lazy_inner is called, we don't
  want to parse or preparse skip_me again. Instead, we want to skip over it,
  since it has already been preparsed once.

  In order to be able to do this, we need to store the information needed for
  allocating the variables in lazy_inner when we preparse it, and then later do
  scope allocation based on that data.

  We need the following data for each scope in lazy_inner's scope tree:
  For each Variable:
  - is_used
  - maybe_assigned
  - has_forced_context_allocation

  For each Scope:
  - inner_scope_calls_eval_.

64 65
  ProducedPreparseData implements storing the above mentioned data and
  ConsumedPreparseData implements restoring it (= setting the context
66 67
  allocation status of the variables in a Scope (and its subscopes) based on the
  data).
68 69 70

 */

71 72 73 74 75
struct PreparseByteDataConstants {
#ifdef DEBUG
  static constexpr int kMagicValue = 0xC0DE0DE;

  static constexpr size_t kUint32Size = 5;
76 77 78
  static constexpr size_t kVarint32MinSize = 3;
  static constexpr size_t kVarint32MaxSize = 7;
  static constexpr size_t kVarint32EndMarker = 0xF1;
79
  static constexpr size_t kUint8Size = 2;
80
  static constexpr size_t kQuarterMarker = 0xF2;
81 82 83
  static constexpr size_t kPlaceholderSize = kUint32Size;
#else
  static constexpr size_t kUint32Size = 4;
84 85
  static constexpr size_t kVarint32MinSize = 1;
  static constexpr size_t kVarint32MaxSize = 5;
86 87 88 89
  static constexpr size_t kUint8Size = 1;
  static constexpr size_t kPlaceholderSize = 0;
#endif

90
  static const size_t kSkippableFunctionMinDataSize =
91 92 93
      4 * kVarint32MinSize + 1 * kUint8Size;
  static const size_t kSkippableFunctionMaxDataSize =
      4 * kVarint32MaxSize + 1 * kUint8Size;
94
};
95

96 97
class V8_EXPORT_PRIVATE PreparseDataBuilder : public ZoneObject,
                                              public PreparseByteDataConstants {
98
 public:
99
  // Create a PreparseDataBuilder object which will collect data as we
100
  // parse.
101 102 103
  explicit PreparseDataBuilder(Zone* zone, PreparseDataBuilder* parent_builder,
                               std::vector<void*>* children_buffer);
  ~PreparseDataBuilder() {}
104 105
  PreparseDataBuilder(const PreparseDataBuilder&) = delete;
  PreparseDataBuilder& operator=(const PreparseDataBuilder&) = delete;
106

107
  PreparseDataBuilder* parent() const { return parent_; }
108

109 110
  // For gathering the inner function data and splitting it up according to the
  // laziness boundaries. Each lazy function gets its own
111
  // ProducedPreparseData, and so do all lazy functions inside it.
112
  class V8_NODISCARD DataGatheringScope {
113
   public:
114 115
    explicit DataGatheringScope(PreParser* preparser)
        : preparser_(preparser), builder_(nullptr) {}
116 117
    DataGatheringScope(const DataGatheringScope&) = delete;
    DataGatheringScope& operator=(const DataGatheringScope&) = delete;
118 119

    void Start(DeclarationScope* function_scope);
120
    void SetSkippableFunction(DeclarationScope* function_scope,
121
                              int function_length, int num_inner_functions);
122 123 124 125
    inline ~DataGatheringScope() {
      if (builder_ == nullptr) return;
      Close();
    }
126 127

   private:
128 129
    void Close();

130
    PreParser* preparser_;
131
    PreparseDataBuilder* builder_;
132
  };
133

134 135
  class V8_EXPORT_PRIVATE ByteData : public ZoneObject,
                                     public PreparseByteDataConstants {
136
   public:
137 138
    ByteData()
        : byte_data_(nullptr), index_(0), free_quarters_in_last_byte_(0) {}
139 140 141 142 143

    void Start(std::vector<uint8_t>* buffer);
    void Finalize(Zone* zone);

    Handle<PreparseData> CopyToHeap(Isolate* isolate, int children_length);
144 145
    Handle<PreparseData> CopyToLocalHeap(LocalIsolate* isolate,
                                         int children_length);
146
    inline ZonePreparseData* CopyToZone(Zone* zone, int children_length);
147

148 149 150 151
    void Reserve(size_t bytes);
    void Add(uint8_t byte);
    int length() const;

152
    void WriteVarint32(uint32_t data);
153 154 155 156
    void WriteUint8(uint8_t data);
    void WriteQuarter(uint8_t data);

#ifdef DEBUG
157
    void WriteUint32(uint32_t data);
158 159 160 161 162 163
    // For overwriting previously written data at position 0.
    void SaveCurrentSizeAtFirstUint32();
#endif

   private:
    union {
164 165 166 167 168
      struct {
        // Only used during construction (is_finalized_ == false).
        std::vector<uint8_t>* byte_data_;
        int index_;
      };
169 170 171 172 173 174 175 176 177 178 179
      // Once the data is finalized, it lives in a Zone, this implies
      // is_finalized_ == true.
      Vector<uint8_t> zone_byte_data_;
    };
    uint8_t free_quarters_in_last_byte_;

#ifdef DEBUG
    bool is_finalized_ = false;
#endif
  };

180 181
  // Saves the information needed for allocating the Scope's (and its
  // subscopes') variables.
182
  void SaveScopeAllocationData(DeclarationScope* scope, Parser* parser);
183

184 185 186 187 188 189 190 191 192 193 194 195 196 197
  // In some cases, PreParser cannot produce the same Scope structure as
  // Parser. If it happens, we're unable to produce the data that would enable
  // skipping the inner functions of that function.
  void Bailout() {
    bailed_out_ = true;
    // We don't need to call Bailout on existing / future children: the only way
    // to try to retrieve their data is through calling Serialize on the parent,
    // and if the parent is bailed out, it won't call Serialize on its children.
  }

  bool bailed_out() const { return bailed_out_; }

#ifdef DEBUG
  bool ThisOrParentBailedOut() const {
198 199
    if (bailed_out_) return true;
    if (parent_ == nullptr) return false;
200 201 202 203
    return parent_->ThisOrParentBailedOut();
  }
#endif  // DEBUG

204
  bool HasInnerFunctions() const;
205
  bool HasData() const;
206
  bool HasDataForParent() const;
207

208
  static bool ScopeNeedsData(Scope* scope);
209 210

 private:
211
  friend class BuilderProducedPreparseData;
212

213
  Handle<PreparseData> Serialize(Isolate* isolate);
214
  Handle<PreparseData> Serialize(LocalIsolate* isolate);
215
  ZonePreparseData* Serialize(Zone* zone);
216

217 218 219
  void FinalizeChildren(Zone* zone);
  void AddChild(PreparseDataBuilder* child);

220
  void SaveDataForScope(Scope* scope);
221
  void SaveDataForVariable(Variable* var);
222
  void SaveDataForInnerScopes(Scope* scope);
223 224 225
  bool SaveDataForSkippableFunction(PreparseDataBuilder* builder);

  void CopyByteData(Zone* zone);
226

227
  PreparseDataBuilder* parent_;
228
  ByteData byte_data_;
229 230 231 232
  union {
    ScopedPtrList<PreparseDataBuilder> children_buffer_;
    Vector<PreparseDataBuilder*> children_;
  };
233

234
  DeclarationScope* function_scope_;
235
  int function_length_;
236 237
  int num_inner_functions_;
  int num_inner_with_data_;
238

239
  // Whether we've given up producing the data for this function.
240 241
  bool bailed_out_ : 1;
  bool has_data_ : 1;
242

243 244 245
#ifdef DEBUG
  bool finalized_children_ = false;
#endif
246
};
247

248
class ProducedPreparseData : public ZoneObject {
249 250 251 252
 public:
  // If there is data (if the Scope contains skippable inner functions), move
  // the data into the heap and return a Handle to it; otherwise return a null
  // MaybeHandle.
253
  virtual Handle<PreparseData> Serialize(Isolate* isolate) = 0;
254

255 256 257
  // If there is data (if the Scope contains skippable inner functions), move
  // the data into the heap and return a Handle to it; otherwise return a null
  // MaybeHandle.
258
  virtual Handle<PreparseData> Serialize(LocalIsolate* isolate) = 0;
259

260
  // If there is data (if the Scope contains skippable inner functions), return
261
  // an off-heap ZonePreparseData representing the data; otherwise
262
  // return nullptr.
263 264 265 266 267 268 269 270 271 272 273 274 275
  virtual ZonePreparseData* Serialize(Zone* zone) = 0;

  // Create a ProducedPreparseData which is a proxy for a previous
  // produced PreparseData in zone.
  static ProducedPreparseData* For(PreparseDataBuilder* builder, Zone* zone);

  // Create a ProducedPreparseData which is a proxy for a previous
  // produced PreparseData on the heap.
  static ProducedPreparseData* For(Handle<PreparseData> data, Zone* zone);

  // Create a ProducedPreparseData which is a proxy for a previous
  // produced PreparseData in zone.
  static ProducedPreparseData* For(ZonePreparseData* data, Zone* zone);
276
};
277

278
class ConsumedPreparseData {
279
 public:
280 281
  // Creates a ConsumedPreparseData representing the data of an on-heap
  // PreparseData |data|.
282 283
  V8_EXPORT_PRIVATE static std::unique_ptr<ConsumedPreparseData> For(
      Isolate* isolate, Handle<PreparseData> data);
284

285 286 287 288
  // Creates a ConsumedPreparseData representing the data of an off-heap
  // ZonePreparseData |data|.
  static std::unique_ptr<ConsumedPreparseData> For(Zone* zone,
                                                   ZonePreparseData* data);
289

290
  virtual ~ConsumedPreparseData() = default;
291

292 293 294
  ConsumedPreparseData(const ConsumedPreparseData&) = delete;
  ConsumedPreparseData& operator=(const ConsumedPreparseData&) = delete;

295
  virtual ProducedPreparseData* GetDataForSkippableFunction(
296
      Zone* zone, int start_position, int* end_position, int* num_parameters,
297
      int* function_length, int* num_inner_functions, bool* uses_super_property,
298
      LanguageMode* language_mode) = 0;
299 300 301

  // Restores the information needed for allocating the Scope's (and its
  // subscopes') variables.
302 303 304
  virtual void RestoreScopeAllocationData(DeclarationScope* scope,
                                          AstValueFactory* ast_value_factory,
                                          Zone* zone) = 0;
305

306
 protected:
307
  ConsumedPreparseData() = default;
308 309 310 311 312
};

}  // namespace internal
}  // namespace v8

313
#endif  // V8_PARSING_PREPARSE_DATA_H_