// 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_PREPARSED_SCOPE_DATA_H_
|
#define V8_PARSING_PREPARSED_SCOPE_DATA_H_
|
|
#include <set>
|
#include <unordered_map>
|
#include <vector>
|
|
#include "src/globals.h"
|
#include "src/handles.h"
|
#include "src/objects/shared-function-info.h"
|
#include "src/zone/zone-chunk-list.h"
|
|
namespace v8 {
|
namespace internal {
|
|
template <typename T>
|
class Handle;
|
|
class PreParser;
|
class PreParsedScopeData;
|
|
/*
|
|
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_.
|
|
ProducedPreParsedScopeData implements storing the above mentioned data and
|
ConsumedPreParsedScopeData implements restoring it (= setting the context
|
allocation status of the variables in a Scope (and its subscopes) based on the
|
data).
|
|
*/
|
|
class ProducedPreParsedScopeData : public ZoneObject {
|
public:
|
class ByteData : public ZoneObject {
|
public:
|
explicit ByteData(Zone* zone)
|
: backing_store_(zone), free_quarters_in_last_byte_(0) {}
|
|
void WriteUint32(uint32_t data);
|
void WriteUint8(uint8_t data);
|
void WriteQuarter(uint8_t data);
|
|
#ifdef DEBUG
|
// For overwriting previously written data at position 0.
|
void OverwriteFirstUint32(uint32_t data);
|
#endif
|
|
Handle<PodArray<uint8_t>> Serialize(Isolate* isolate);
|
|
size_t size() const { return backing_store_.size(); }
|
|
private:
|
ZoneChunkList<uint8_t> backing_store_;
|
uint8_t free_quarters_in_last_byte_;
|
};
|
|
// Create a ProducedPreParsedScopeData object which will collect data as we
|
// parse.
|
ProducedPreParsedScopeData(Zone* zone, ProducedPreParsedScopeData* parent);
|
|
// Create a ProducedPreParsedScopeData which is just a proxy for a previous
|
// produced PreParsedScopeData.
|
ProducedPreParsedScopeData(Handle<PreParsedScopeData> data, Zone* zone);
|
|
ProducedPreParsedScopeData* 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
|
// ProducedPreParsedScopeData, and so do all lazy functions inside it.
|
class DataGatheringScope {
|
public:
|
DataGatheringScope(DeclarationScope* function_scope, PreParser* preparser);
|
~DataGatheringScope();
|
|
void MarkFunctionAsSkippable(int end_position, int num_inner_functions);
|
|
private:
|
DeclarationScope* function_scope_;
|
PreParser* preparser_;
|
ProducedPreParsedScopeData* produced_preparsed_scope_data_;
|
|
DISALLOW_COPY_AND_ASSIGN(DataGatheringScope);
|
};
|
|
// Saves the information needed for allocating the Scope's (and its
|
// subscopes') variables.
|
void SaveScopeAllocationData(DeclarationScope* scope);
|
|
// 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 ContainsInnerFunctions() const;
|
|
// 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.
|
MaybeHandle<PreParsedScopeData> Serialize(Isolate* isolate);
|
|
static bool ScopeNeedsData(Scope* scope);
|
static bool ScopeIsSkippableFunctionScope(Scope* scope);
|
|
private:
|
void AddSkippableFunction(int start_position, int end_position,
|
int num_parameters, int num_inner_functions,
|
LanguageMode language_mode,
|
bool uses_super_property);
|
|
void SaveDataForScope(Scope* scope);
|
void SaveDataForVariable(Variable* var);
|
void SaveDataForInnerScopes(Scope* scope);
|
|
ProducedPreParsedScopeData* parent_;
|
|
ByteData* byte_data_;
|
ZoneChunkList<ProducedPreParsedScopeData*> data_for_inner_functions_;
|
|
// Whether we've given up producing the data for this function.
|
bool bailed_out_;
|
|
// ProducedPreParsedScopeData can also mask a Handle<PreParsedScopeData>
|
// which was produced already earlier. This happens for deeper lazy functions.
|
Handle<PreParsedScopeData> previously_produced_preparsed_scope_data_;
|
|
DISALLOW_COPY_AND_ASSIGN(ProducedPreParsedScopeData);
|
};
|
|
class ConsumedPreParsedScopeData {
|
public:
|
class ByteData {
|
public:
|
ByteData()
|
: data_(nullptr), index_(0), stored_quarters_(0), stored_byte_(0) {}
|
|
// Reading from the ByteData is only allowed when a ReadingScope is on the
|
// stack. This ensures that we have a DisallowHeapAllocation in place
|
// whenever ByteData holds a raw pointer into the heap.
|
class ReadingScope {
|
public:
|
ReadingScope(ByteData* consumed_data, PodArray<uint8_t>* data)
|
: consumed_data_(consumed_data) {
|
consumed_data->data_ = data;
|
}
|
explicit ReadingScope(ConsumedPreParsedScopeData* parent);
|
~ReadingScope() { consumed_data_->data_ = nullptr; }
|
|
private:
|
ByteData* consumed_data_;
|
DisallowHeapAllocation no_gc;
|
};
|
|
void SetPosition(int position) { index_ = position; }
|
|
int32_t ReadUint32();
|
uint8_t ReadUint8();
|
uint8_t ReadQuarter();
|
|
size_t RemainingBytes() const;
|
|
// private:
|
PodArray<uint8_t>* data_;
|
int index_;
|
uint8_t stored_quarters_;
|
uint8_t stored_byte_;
|
};
|
|
ConsumedPreParsedScopeData();
|
~ConsumedPreParsedScopeData();
|
|
void SetData(Isolate* isolate, Handle<PreParsedScopeData> data);
|
|
bool HasData() const { return !data_.is_null(); }
|
|
ProducedPreParsedScopeData* GetDataForSkippableFunction(
|
Zone* zone, int start_position, int* end_position, int* num_parameters,
|
int* num_inner_functions, bool* uses_super_property,
|
LanguageMode* language_mode);
|
|
// Restores the information needed for allocating the Scope's (and its
|
// subscopes') variables.
|
void RestoreScopeAllocationData(DeclarationScope* scope);
|
|
private:
|
void RestoreData(Scope* scope);
|
void RestoreDataForVariable(Variable* var);
|
void RestoreDataForInnerScopes(Scope* scope);
|
|
Isolate* isolate_;
|
Handle<PreParsedScopeData> data_;
|
std::unique_ptr<ByteData> scope_data_;
|
// When consuming the data, these indexes point to the data we're going to
|
// consume next.
|
int child_index_;
|
|
DISALLOW_COPY_AND_ASSIGN(ConsumedPreParsedScopeData);
|
};
|
|
} // namespace internal
|
} // namespace v8
|
|
#endif // V8_PARSING_PREPARSED_SCOPE_DATA_H_
|