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_PREPARSE_DATA_H_
6 #define V8_PARSING_PREPARSE_DATA_H_
7 
8 #include <memory>
9 
10 #include "src/common/globals.h"
11 #include "src/handles/handles.h"
12 #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 namespace v8 {
19 namespace internal {
20 
21 template <typename T>
22 class PodArray;
23 
24 class Parser;
25 class PreParser;
26 class PreparseData;
27 class ZonePreparseData;
28 class AstValueFactory;
29 
30 /*
31 
32   Skipping inner functions.
33 
34   Consider the following code:
35   (function eager_outer() {
36     function lazy_inner() {
37       let a;
38       function skip_me() { a; }
39     }
40 
41     return lazy_inner;
42   })();
43 
44   ... lazy_inner(); ...
45 
46   When parsing the code the first time, eager_outer is parsed and lazy_inner
47   (and everything inside it) is preparsed. When lazy_inner is called, we don't
48   want to parse or preparse skip_me again. Instead, we want to skip over it,
49   since it has already been preparsed once.
50 
51   In order to be able to do this, we need to store the information needed for
52   allocating the variables in lazy_inner when we preparse it, and then later do
53   scope allocation based on that data.
54 
55   We need the following data for each scope in lazy_inner's scope tree:
56   For each Variable:
57   - is_used
58   - maybe_assigned
59   - has_forced_context_allocation
60 
61   For each Scope:
62   - inner_scope_calls_eval_.
63 
64   ProducedPreparseData implements storing the above mentioned data and
65   ConsumedPreparseData implements restoring it (= setting the context
66   allocation status of the variables in a Scope (and its subscopes) based on the
67   data).
68 
69  */
70 
71 struct PreparseByteDataConstants {
72 #ifdef DEBUG
73   static constexpr int kMagicValue = 0xC0DE0DE;
74 
75   static constexpr size_t kUint32Size = 5;
76   static constexpr size_t kVarint32MinSize = 3;
77   static constexpr size_t kVarint32MaxSize = 7;
78   static constexpr size_t kVarint32EndMarker = 0xF1;
79   static constexpr size_t kUint8Size = 2;
80   static constexpr size_t kQuarterMarker = 0xF2;
81   static constexpr size_t kPlaceholderSize = kUint32Size;
82 #else
83   static constexpr size_t kUint32Size = 4;
84   static constexpr size_t kVarint32MinSize = 1;
85   static constexpr size_t kVarint32MaxSize = 5;
86   static constexpr size_t kUint8Size = 1;
87   static constexpr size_t kPlaceholderSize = 0;
88 #endif
89 
90   static const size_t kSkippableFunctionMinDataSize =
91       4 * kVarint32MinSize + 1 * kUint8Size;
92   static const size_t kSkippableFunctionMaxDataSize =
93       4 * kVarint32MaxSize + 1 * kUint8Size;
94 };
95 
96 class V8_EXPORT_PRIVATE PreparseDataBuilder : public ZoneObject,
97                                               public PreparseByteDataConstants {
98  public:
99   // Create a PreparseDataBuilder object which will collect data as we
100   // parse.
101   explicit PreparseDataBuilder(Zone* zone, PreparseDataBuilder* parent_builder,
102                                std::vector<void*>* children_buffer);
~PreparseDataBuilder()103   ~PreparseDataBuilder() {}
104 
parent()105   PreparseDataBuilder* parent() const { return parent_; }
106 
107   // For gathering the inner function data and splitting it up according to the
108   // laziness boundaries. Each lazy function gets its own
109   // ProducedPreparseData, and so do all lazy functions inside it.
110   class DataGatheringScope {
111    public:
DataGatheringScope(PreParser * preparser)112     explicit DataGatheringScope(PreParser* preparser)
113         : preparser_(preparser), builder_(nullptr) {}
114 
115     void Start(DeclarationScope* function_scope);
116     void SetSkippableFunction(DeclarationScope* function_scope,
117                               int function_length, int num_inner_functions);
~DataGatheringScope()118     inline ~DataGatheringScope() {
119       if (builder_ == nullptr) return;
120       Close();
121     }
122 
123    private:
124     void Close();
125 
126     PreParser* preparser_;
127     PreparseDataBuilder* builder_;
128 
129     DISALLOW_COPY_AND_ASSIGN(DataGatheringScope);
130   };
131 
132   class V8_EXPORT_PRIVATE ByteData : public ZoneObject,
133                                      public PreparseByteDataConstants {
134    public:
ByteData()135     ByteData()
136         : byte_data_(nullptr), index_(0), free_quarters_in_last_byte_(0) {}
137 
138     void Start(std::vector<uint8_t>* buffer);
139     void Finalize(Zone* zone);
140 
141     Handle<PreparseData> CopyToHeap(Isolate* isolate, int children_length);
142     Handle<PreparseData> CopyToLocalHeap(LocalIsolate* isolate,
143                                          int children_length);
144     inline ZonePreparseData* CopyToZone(Zone* zone, int children_length);
145 
146     void Reserve(size_t bytes);
147     void Add(uint8_t byte);
148     int length() const;
149 
150     void WriteVarint32(uint32_t data);
151     void WriteUint8(uint8_t data);
152     void WriteQuarter(uint8_t data);
153 
154 #ifdef DEBUG
155     void WriteUint32(uint32_t data);
156     // For overwriting previously written data at position 0.
157     void SaveCurrentSizeAtFirstUint32();
158 #endif
159 
160    private:
161     union {
162       struct {
163         // Only used during construction (is_finalized_ == false).
164         std::vector<uint8_t>* byte_data_;
165         int index_;
166       };
167       // Once the data is finalized, it lives in a Zone, this implies
168       // is_finalized_ == true.
169       Vector<uint8_t> zone_byte_data_;
170     };
171     uint8_t free_quarters_in_last_byte_;
172 
173 #ifdef DEBUG
174     bool is_finalized_ = false;
175 #endif
176   };
177 
178   // Saves the information needed for allocating the Scope's (and its
179   // subscopes') variables.
180   void SaveScopeAllocationData(DeclarationScope* scope, Parser* parser);
181 
182   // In some cases, PreParser cannot produce the same Scope structure as
183   // Parser. If it happens, we're unable to produce the data that would enable
184   // skipping the inner functions of that function.
Bailout()185   void Bailout() {
186     bailed_out_ = true;
187     // We don't need to call Bailout on existing / future children: the only way
188     // to try to retrieve their data is through calling Serialize on the parent,
189     // and if the parent is bailed out, it won't call Serialize on its children.
190   }
191 
bailed_out()192   bool bailed_out() const { return bailed_out_; }
193 
194 #ifdef DEBUG
ThisOrParentBailedOut()195   bool ThisOrParentBailedOut() const {
196     if (bailed_out_) return true;
197     if (parent_ == nullptr) return false;
198     return parent_->ThisOrParentBailedOut();
199   }
200 #endif  // DEBUG
201 
202   bool HasInnerFunctions() const;
203   bool HasData() const;
204   bool HasDataForParent() const;
205 
206   static bool ScopeNeedsData(Scope* scope);
207 
208  private:
209   friend class BuilderProducedPreparseData;
210 
211   Handle<PreparseData> Serialize(Isolate* isolate);
212   Handle<PreparseData> Serialize(LocalIsolate* isolate);
213   ZonePreparseData* Serialize(Zone* zone);
214 
215   void FinalizeChildren(Zone* zone);
216   void AddChild(PreparseDataBuilder* child);
217 
218   void SaveDataForScope(Scope* scope);
219   void SaveDataForVariable(Variable* var);
220   void SaveDataForInnerScopes(Scope* scope);
221   bool SaveDataForSkippableFunction(PreparseDataBuilder* builder);
222 
223   void CopyByteData(Zone* zone);
224 
225   PreparseDataBuilder* parent_;
226   ByteData byte_data_;
227   union {
228     ScopedPtrList<PreparseDataBuilder> children_buffer_;
229     Vector<PreparseDataBuilder*> children_;
230   };
231 
232   DeclarationScope* function_scope_;
233   int function_length_;
234   int num_inner_functions_;
235   int num_inner_with_data_;
236 
237   // Whether we've given up producing the data for this function.
238   bool bailed_out_ : 1;
239   bool has_data_ : 1;
240 
241 #ifdef DEBUG
242   bool finalized_children_ = false;
243 #endif
244 
245   DISALLOW_COPY_AND_ASSIGN(PreparseDataBuilder);
246 };
247 
248 class ProducedPreparseData : public ZoneObject {
249  public:
250   // If there is data (if the Scope contains skippable inner functions), move
251   // the data into the heap and return a Handle to it; otherwise return a null
252   // MaybeHandle.
253   virtual Handle<PreparseData> Serialize(Isolate* isolate) = 0;
254 
255   // If there is data (if the Scope contains skippable inner functions), move
256   // the data into the heap and return a Handle to it; otherwise return a null
257   // 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   virtual ZonePreparseData* Serialize(Zone* zone) = 0;
264 
265   // Create a ProducedPreparseData which is a proxy for a previous
266   // produced PreparseData in zone.
267   static ProducedPreparseData* For(PreparseDataBuilder* builder, Zone* zone);
268 
269   // Create a ProducedPreparseData which is a proxy for a previous
270   // produced PreparseData on the heap.
271   static ProducedPreparseData* For(Handle<PreparseData> data, Zone* zone);
272 
273   // Create a ProducedPreparseData which is a proxy for a previous
274   // produced PreparseData in zone.
275   static ProducedPreparseData* For(ZonePreparseData* data, Zone* zone);
276 };
277 
278 class ConsumedPreparseData {
279  public:
280   // Creates a ConsumedPreparseData representing the data of an on-heap
281   // PreparseData |data|.
282   V8_EXPORT_PRIVATE static std::unique_ptr<ConsumedPreparseData> For(
283       Isolate* isolate, Handle<PreparseData> data);
284 
285   // Creates a ConsumedPreparseData representing the data of an off-heap
286   // ZonePreparseData |data|.
287   static std::unique_ptr<ConsumedPreparseData> For(Zone* zone,
288                                                    ZonePreparseData* data);
289 
290   virtual ~ConsumedPreparseData() = default;
291 
292   virtual ProducedPreparseData* GetDataForSkippableFunction(
293       Zone* zone, int start_position, int* end_position, int* num_parameters,
294       int* function_length, int* num_inner_functions, bool* uses_super_property,
295       LanguageMode* language_mode) = 0;
296 
297   // Restores the information needed for allocating the Scope's (and its
298   // subscopes') variables.
299   virtual void RestoreScopeAllocationData(DeclarationScope* scope,
300                                           AstValueFactory* ast_value_factory,
301                                           Zone* zone) = 0;
302 
303  protected:
304   ConsumedPreparseData() = default;
305 
306  private:
307   DISALLOW_COPY_AND_ASSIGN(ConsumedPreparseData);
308 };
309 
310 }  // namespace internal
311 }  // namespace v8
312 
313 #endif  // V8_PARSING_PREPARSE_DATA_H_
314