| // 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. |
| |
| #ifndef V8_PARSING_PREPARSE_DATA_H_ |
| #define V8_PARSING_PREPARSE_DATA_H_ |
| |
| #include <memory> |
| |
| #include "src/base/vector.h" |
| #include "src/common/globals.h" |
| #include "src/handles/handles.h" |
| #include "src/handles/maybe-handles.h" |
| #include "src/utils/scoped-list.h" |
| #include "src/zone/zone-chunk-list.h" |
| #include "src/zone/zone-containers.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| template <typename T> |
| class PodArray; |
| |
| class Parser; |
| class PreParser; |
| class PreparseData; |
| class ZonePreparseData; |
| class AstValueFactory; |
| |
| /* |
| |
| 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_. |
| |
| ProducedPreparseData implements storing the above mentioned data and |
| ConsumedPreparseData implements restoring it (= setting the context |
| allocation status of the variables in a Scope (and its subscopes) based on the |
| data). |
| |
| */ |
| |
| struct PreparseByteDataConstants { |
| #ifdef DEBUG |
| static constexpr int kMagicValue = 0xC0DE0DE; |
| |
| static constexpr size_t kUint32Size = 5; |
| static constexpr size_t kVarint32MinSize = 3; |
| static constexpr size_t kVarint32MaxSize = 7; |
| static constexpr size_t kVarint32EndMarker = 0xF1; |
| static constexpr size_t kUint8Size = 2; |
| static constexpr size_t kQuarterMarker = 0xF2; |
| static constexpr size_t kPlaceholderSize = kUint32Size; |
| #else |
| static constexpr size_t kUint32Size = 4; |
| static constexpr size_t kVarint32MinSize = 1; |
| static constexpr size_t kVarint32MaxSize = 5; |
| static constexpr size_t kUint8Size = 1; |
| static constexpr size_t kPlaceholderSize = 0; |
| #endif |
| |
| static const size_t kSkippableFunctionMinDataSize = |
| 4 * kVarint32MinSize + 1 * kUint8Size; |
| static const size_t kSkippableFunctionMaxDataSize = |
| 4 * kVarint32MaxSize + 1 * kUint8Size; |
| }; |
| |
| class V8_EXPORT_PRIVATE PreparseDataBuilder : public ZoneObject, |
| public PreparseByteDataConstants { |
| public: |
| // Create a PreparseDataBuilder object which will collect data as we |
| // parse. |
| explicit PreparseDataBuilder(Zone* zone, PreparseDataBuilder* parent_builder, |
| std::vector<void*>* children_buffer); |
| ~PreparseDataBuilder() {} |
| PreparseDataBuilder(const PreparseDataBuilder&) = delete; |
| PreparseDataBuilder& operator=(const PreparseDataBuilder&) = delete; |
| |
| PreparseDataBuilder* parent() const { return parent_; } |
| |
| // For gathering the inner function data and splitting it up according to the |
| // laziness boundaries. Each lazy function gets its own |
| // ProducedPreparseData, and so do all lazy functions inside it. |
| class V8_NODISCARD DataGatheringScope { |
| public: |
| explicit DataGatheringScope(PreParser* preparser) |
| : preparser_(preparser), builder_(nullptr) {} |
| DataGatheringScope(const DataGatheringScope&) = delete; |
| DataGatheringScope& operator=(const DataGatheringScope&) = delete; |
| |
| void Start(DeclarationScope* function_scope); |
| void SetSkippableFunction(DeclarationScope* function_scope, |
| int function_length, int num_inner_functions); |
| inline ~DataGatheringScope() { |
| if (builder_ == nullptr) return; |
| Close(); |
| } |
| |
| private: |
| void Close(); |
| |
| PreParser* preparser_; |
| PreparseDataBuilder* builder_; |
| }; |
| |
| class V8_EXPORT_PRIVATE ByteData : public ZoneObject, |
| public PreparseByteDataConstants { |
| public: |
| ByteData() |
| : byte_data_(nullptr), index_(0), free_quarters_in_last_byte_(0) {} |
| |
| void Start(std::vector<uint8_t>* buffer); |
| void Finalize(Zone* zone); |
| |
| Handle<PreparseData> CopyToHeap(Isolate* isolate, int children_length); |
| Handle<PreparseData> CopyToLocalHeap(LocalIsolate* isolate, |
| int children_length); |
| inline ZonePreparseData* CopyToZone(Zone* zone, int children_length); |
| |
| void Reserve(size_t bytes); |
| void Add(uint8_t byte); |
| int length() const; |
| |
| void WriteVarint32(uint32_t data); |
| void WriteUint8(uint8_t data); |
| void WriteQuarter(uint8_t data); |
| |
| #ifdef DEBUG |
| void WriteUint32(uint32_t data); |
| // For overwriting previously written data at position 0. |
| void SaveCurrentSizeAtFirstUint32(); |
| #endif |
| |
| private: |
| union { |
| struct { |
| // Only used during construction (is_finalized_ == false). |
| std::vector<uint8_t>* byte_data_; |
| int index_; |
| }; |
| // Once the data is finalized, it lives in a Zone, this implies |
| // is_finalized_ == true. |
| base::Vector<uint8_t> zone_byte_data_; |
| }; |
| uint8_t free_quarters_in_last_byte_; |
| |
| #ifdef DEBUG |
| bool is_finalized_ = false; |
| #endif |
| }; |
| |
| // Saves the information needed for allocating the Scope's (and its |
| // subscopes') variables. |
| void SaveScopeAllocationData(DeclarationScope* scope, Parser* parser); |
| |
| // 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 { |
| if (bailed_out_) return true; |
| if (parent_ == nullptr) return false; |
| return parent_->ThisOrParentBailedOut(); |
| } |
| #endif // DEBUG |
| |
| bool HasInnerFunctions() const; |
| bool HasData() const; |
| bool HasDataForParent() const; |
| |
| static bool ScopeNeedsData(Scope* scope); |
| |
| private: |
| friend class BuilderProducedPreparseData; |
| |
| Handle<PreparseData> Serialize(Isolate* isolate); |
| Handle<PreparseData> Serialize(LocalIsolate* isolate); |
| ZonePreparseData* Serialize(Zone* zone); |
| |
| void FinalizeChildren(Zone* zone); |
| void AddChild(PreparseDataBuilder* child); |
| |
| void SaveDataForScope(Scope* scope); |
| void SaveDataForVariable(Variable* var); |
| void SaveDataForInnerScopes(Scope* scope); |
| bool SaveDataForSkippableFunction(PreparseDataBuilder* builder); |
| |
| void CopyByteData(Zone* zone); |
| |
| PreparseDataBuilder* parent_; |
| ByteData byte_data_; |
| union { |
| ScopedPtrList<PreparseDataBuilder> children_buffer_; |
| base::Vector<PreparseDataBuilder*> children_; |
| }; |
| |
| DeclarationScope* function_scope_; |
| int function_length_; |
| int num_inner_functions_; |
| int num_inner_with_data_; |
| |
| // Whether we've given up producing the data for this function. |
| bool bailed_out_ : 1; |
| bool has_data_ : 1; |
| |
| #ifdef DEBUG |
| bool finalized_children_ = false; |
| #endif |
| }; |
| |
| class ProducedPreparseData : public ZoneObject { |
| 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. |
| virtual Handle<PreparseData> Serialize(Isolate* isolate) = 0; |
| |
| // 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. |
| virtual Handle<PreparseData> Serialize(LocalIsolate* isolate) = 0; |
| |
| // If there is data (if the Scope contains skippable inner functions), return |
| // an off-heap ZonePreparseData representing the data; otherwise |
| // return nullptr. |
| 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); |
| }; |
| |
| class ConsumedPreparseData { |
| public: |
| // Creates a ConsumedPreparseData representing the data of an on-heap |
| // PreparseData |data|. |
| V8_EXPORT_PRIVATE static std::unique_ptr<ConsumedPreparseData> For( |
| Isolate* isolate, Handle<PreparseData> data); |
| V8_EXPORT_PRIVATE static std::unique_ptr<ConsumedPreparseData> For( |
| LocalIsolate* isolate, Handle<PreparseData> data); |
| |
| // Creates a ConsumedPreparseData representing the data of an off-heap |
| // ZonePreparseData |data|. |
| static std::unique_ptr<ConsumedPreparseData> For(Zone* zone, |
| ZonePreparseData* data); |
| |
| virtual ~ConsumedPreparseData() = default; |
| |
| ConsumedPreparseData(const ConsumedPreparseData&) = delete; |
| ConsumedPreparseData& operator=(const ConsumedPreparseData&) = delete; |
| |
| virtual ProducedPreparseData* GetDataForSkippableFunction( |
| Zone* zone, int start_position, int* end_position, int* num_parameters, |
| int* function_length, int* num_inner_functions, bool* uses_super_property, |
| LanguageMode* language_mode) = 0; |
| |
| // Restores the information needed for allocating the Scope's (and its |
| // subscopes') variables. |
| virtual void RestoreScopeAllocationData(DeclarationScope* scope, |
| AstValueFactory* ast_value_factory, |
| Zone* zone) = 0; |
| |
| protected: |
| ConsumedPreparseData() = default; |
| }; |
| |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_PARSING_PREPARSE_DATA_H_ |