[parser] Use shared data buffer for PreparseData generation

By using a shared byte buffer on the preparser we can drastically
reduce the number of ZoneChunkLists.

Each PreparseDataBuilder now explicitly keeps track of all inner
builders/functions and writes out the data in consecutive order.

Change-Id: I0aada118d869b150108c1f633d9960474ad2f9a1
Reviewed-on: https://chromium-review.googlesource.com/c/1411600
Commit-Queue: Toon Verwaest <verwaest@chromium.org>
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58926}
This commit is contained in:
Camillo Bruni 2019-01-18 14:14:56 +01:00 committed by Commit Bot
parent 1a95d4de81
commit 61b217b1cb
10 changed files with 381 additions and 271 deletions

View File

@ -16,6 +16,7 @@
#include "src/objects/module-inl.h"
#include "src/objects/scope-info.h"
#include "src/parsing/parse-info.h"
#include "src/parsing/parser.h"
#include "src/parsing/preparse-data.h"
#include "src/zone/zone-list-inl.h"
@ -1417,28 +1418,29 @@ void DeclarationScope::ResetAfterPreparsing(AstValueFactory* ast_value_factory,
was_lazily_parsed_ = !aborted;
}
void Scope::SavePreparseData() {
void Scope::SavePreparseData(Parser* parser) {
if (PreparseDataBuilder::ScopeIsSkippableFunctionScope(this)) {
AsDeclarationScope()->SavePreparseDataForDeclarationScope();
AsDeclarationScope()->SavePreparseDataForDeclarationScope(parser);
}
for (Scope* scope = inner_scope_; scope != nullptr; scope = scope->sibling_) {
scope->SavePreparseData();
scope->SavePreparseData(parser);
}
}
void DeclarationScope::SavePreparseDataForDeclarationScope() {
void DeclarationScope::SavePreparseDataForDeclarationScope(Parser* parser) {
if (preparse_data_builder_ == nullptr) return;
preparse_data_builder_->SaveScopeAllocationData(this);
preparse_data_builder_->SaveScopeAllocationData(this, parser);
}
void DeclarationScope::AnalyzePartially(AstNodeFactory* ast_node_factory) {
void DeclarationScope::AnalyzePartially(Parser* parser,
AstNodeFactory* ast_node_factory) {
DCHECK(!force_eager_compilation_);
UnresolvedList new_unresolved_list;
if (!IsArrowFunction(function_kind_) &&
(!outer_scope_->is_script_scope() ||
(preparse_data_builder_ != nullptr &&
preparse_data_builder_->ContainsInnerFunctions()))) {
preparse_data_builder_->HasInnerFunctions()))) {
// Try to resolve unresolved variables for this Scope and migrate those
// which cannot be resolved inside. It doesn't make sense to try to resolve
// them in the outer Scopes here, because they are incomplete.
@ -1449,7 +1451,7 @@ void DeclarationScope::AnalyzePartially(AstNodeFactory* ast_node_factory) {
function_ = ast_node_factory->CopyVariable(function_);
}
SavePreparseData();
SavePreparseData(parser);
}
#ifdef DEBUG

View File

@ -21,6 +21,7 @@ class AstValueFactory;
class AstRawString;
class Declaration;
class ParseInfo;
class Parser;
class PreparseDataBuilder;
class SloppyBlockFunctionStatement;
class Statement;
@ -571,7 +572,7 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
// Walk the scope chain to find DeclarationScopes; call
// SavePreparseDataForDeclarationScope for each.
void SavePreparseData();
void SavePreparseData(Parser* parser);
// Create a non-local variable with a given name.
// These variables are looked up dynamically at runtime.
@ -949,7 +950,7 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
// this records variables which cannot be resolved inside the Scope (we don't
// yet know what they will resolve to since the outer Scopes are incomplete)
// and recreates them with the correct Zone with ast_node_factory.
void AnalyzePartially(AstNodeFactory* ast_node_factory);
void AnalyzePartially(Parser* parser, AstNodeFactory* ast_node_factory);
// Allocate ScopeInfos for top scope and any inner scopes that need them.
// Does nothing if ScopeInfo is already allocated.
@ -998,7 +999,7 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
// Save data describing the context allocation of the variables in this scope
// and its subscopes (except scopes at the laziness boundary). The data is
// saved in produced_preparse_data_.
void SavePreparseDataForDeclarationScope();
void SavePreparseDataForDeclarationScope(Parser* parser);
void set_preparse_data_builder(PreparseDataBuilder* preparse_data_builder) {
preparse_data_builder_ = preparse_data_builder;

View File

@ -560,15 +560,20 @@ class PodArray : public ByteArray {
ByteArray::copy_out(index * sizeof(T), reinterpret_cast<byte*>(result),
sizeof(T));
}
void copy_in(int index, const T* buffer, int length) {
ByteArray::copy_in(index * sizeof(T), reinterpret_cast<const byte*>(buffer),
length * sizeof(T));
}
T get(int index) {
T result;
copy_out(index, &result);
return result;
}
void set(int index, const T& value) {
copy_in(index * sizeof(T), reinterpret_cast<const byte*>(&value),
sizeof(T));
}
void set(int index, const T& value) { copy_in(index, &value, 1); }
inline int length() const;
DECL_CAST(PodArray<T>)

View File

@ -395,6 +395,7 @@ Parser::Parser(ParseInfo* info)
target_stack_(nullptr),
total_preparse_skipped_(0),
consumed_preparse_data_(info->consumed_preparse_data()),
preparse_data_buffer_(),
parameters_end_pos_(info->parameters_end_pos()) {
// Even though we were passed ParseInfo, we should not store it in
// Parser - this makes sure that Isolate is not accidentally accessed via
@ -2545,7 +2546,7 @@ bool Parser::SkipFunction(const AstRawString* function_name, FunctionKind kind,
function_scope->end_position() - function_scope->start_position();
*num_parameters = logger->num_parameters();
SkipFunctionLiterals(logger->num_inner_functions());
function_scope->AnalyzePartially(factory());
function_scope->AnalyzePartially(this, factory());
}
return true;

View File

@ -282,6 +282,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
SET_ALLOW(harmony_private_methods);
SET_ALLOW(eval_cache);
#undef SET_ALLOW
preparse_data_buffer_.reserve(128);
}
return reusable_preparser_;
}
@ -1055,8 +1056,13 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
ParseInfo* info() const { return info_; }
std::vector<uint8_t>* preparse_data_buffer() {
return &preparse_data_buffer_;
}
// Parser's private field members.
friend class PreParserZoneScope; // Uses reusable_preparser().
friend class PreparseDataBuilder; // Uses preparse_data_buffer()
ParseInfo* info_;
Scanner scanner_;
@ -1082,6 +1088,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
bool allow_lazy_;
bool temp_zoned_;
ConsumedPreparseData* consumed_preparse_data_;
std::vector<uint8_t> preparse_data_buffer_;
// If not kNoSourcePosition, indicates that the first function literal
// encountered is a dynamic function, see CreateDynamicFunction(). This field

View File

@ -15,51 +15,6 @@ namespace internal {
// Classes which are internal to prepared-scope-data.cc, but are exposed in
// a header for tests.
struct PreparseByteDataConstants {
#ifdef DEBUG
static constexpr int kMagicValue = 0xC0DE0DE;
static constexpr size_t kUint32Size = 5;
static constexpr size_t kUint8Size = 2;
static constexpr size_t kQuarterMarker = 0;
static constexpr size_t kPlaceholderSize = kUint32Size;
#else
static constexpr size_t kUint32Size = 4;
static constexpr size_t kUint8Size = 1;
static constexpr size_t kPlaceholderSize = 0;
#endif
static const size_t kSkippableFunctionDataSize =
4 * kUint32Size + 1 * kUint8Size;
};
class PreparseDataBuilder::ByteData : public ZoneObject,
public PreparseByteDataConstants {
public:
explicit ByteData(Zone* zone)
: free_quarters_in_last_byte_(0), backing_store_(zone) {}
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
void StoreInto(PreparseData data);
size_t size() const { return backing_store_.size(); }
ZoneChunkList<uint8_t>::iterator begin() { return backing_store_.begin(); }
ZoneChunkList<uint8_t>::iterator end() { return backing_store_.end(); }
private:
uint8_t free_quarters_in_last_byte_;
ZoneChunkList<uint8_t> backing_store_;
};
// Wraps a ZoneVector<uint8_t> to have with functions named the same as
// PodArray<uint8_t>.
class ZoneVectorWrapper {
@ -124,6 +79,7 @@ class BaseConsumedPreparseData : public ConsumedPreparseData {
}
int32_t ReadUint32() {
DCHECK(has_data_);
DCHECK(HasRemainingBytes(kUint32Size));
// Check that there indeed is an integer following.
DCHECK_EQ(data_.get(index_++), kUint32Size);
@ -208,7 +164,7 @@ class OnHeapConsumedPreparseData final
OnHeapConsumedPreparseData(Isolate* isolate, Handle<PreparseData> data);
PreparseData GetScopeData() final;
ProducedPreparseData* GetChildData(Zone* zone, int index) final;
ProducedPreparseData* GetChildData(Zone* zone, int child_index) final;
private:
Isolate* isolate_;
@ -218,16 +174,16 @@ class OnHeapConsumedPreparseData final
// A serialized PreparseData in zone memory (as apposed to being on-heap).
class ZonePreparseData : public ZoneObject {
public:
ZonePreparseData(Zone* zone, PreparseDataBuilder::ByteData* byte_data,
int child_length);
ZonePreparseData(Zone* zone, Vector<uint8_t>* byte_data, int child_length);
Handle<PreparseData> Serialize(Isolate* isolate);
int child_length() const { return static_cast<int>(children_.size()); }
int children_length() const { return static_cast<int>(children_.size()); }
ZonePreparseData* get_child(int index) { return children_[index]; }
void set_child(int index, ZonePreparseData* child) {
DCHECK_NOT_NULL(child);
children_[index] = child;
}

View File

@ -11,6 +11,7 @@
#include "src/handles.h"
#include "src/objects-inl.h"
#include "src/objects/shared-function-info.h"
#include "src/parsing/parser.h"
#include "src/parsing/preparse-data-impl.h"
#include "src/parsing/preparser.h"
@ -82,199 +83,136 @@ STATIC_ASSERT(LanguageModeSize <= LanguageField::kNumValues);
*/
void PreparseDataBuilder::ByteData::WriteUint32(uint32_t data) {
#ifdef DEBUG
// Save expected item size in debug mode.
backing_store_.push_back(kUint32Size);
#endif
backing_store_.push_back(data & 0xFF);
backing_store_.push_back((data >> 8) & 0xFF);
backing_store_.push_back((data >> 16) & 0xFF);
backing_store_.push_back((data >> 24) & 0xFF);
free_quarters_in_last_byte_ = 0;
}
#ifdef DEBUG
void PreparseDataBuilder::ByteData::OverwriteFirstUint32(uint32_t data) {
auto it = backing_store_.begin();
// Check that that position already holds an item of the expected size.
DCHECK_GE(backing_store_.size(), kUint32Size);
DCHECK_EQ(*it, kUint32Size);
++it;
*it++ = data & 0xFF;
*it++ = (data >> 8) & 0xFF;
*it++ = (data >> 16) & 0xFF;
*it++ = (data >> 24) & 0xFF;
}
#endif
void PreparseDataBuilder::ByteData::WriteUint8(uint8_t data) {
#ifdef DEBUG
// Save expected item size in debug mode.
backing_store_.push_back(kUint8Size);
#endif
backing_store_.push_back(data);
free_quarters_in_last_byte_ = 0;
}
void PreparseDataBuilder::ByteData::WriteQuarter(uint8_t data) {
DCHECK_LE(data, 3);
if (free_quarters_in_last_byte_ == 0) {
#ifdef DEBUG
// Save a marker in debug mode.
backing_store_.push_back(kQuarterMarker);
#endif
backing_store_.push_back(0);
free_quarters_in_last_byte_ = 3;
} else {
--free_quarters_in_last_byte_;
}
uint8_t shift_amount = free_quarters_in_last_byte_ * 2;
DCHECK_EQ(backing_store_.back() & (3 << shift_amount), 0);
backing_store_.back() |= (data << shift_amount);
}
void PreparseDataBuilder::ByteData::StoreInto(PreparseData data) {
DisallowHeapAllocation no_gc;
int i = 0;
for (uint8_t item : backing_store_) {
data->set(i++, item);
}
}
PreparseDataBuilder::PreparseDataBuilder(Zone* zone,
PreparseDataBuilder* parent)
: parent_(parent),
byte_data_(new (zone) ByteData(zone)),
data_for_inner_functions_(zone),
bailed_out_(false) {
#ifdef DEBUG
// Reserve space for scope_data_start, written later:
byte_data_->WriteUint32(0);
#endif
}
PreparseDataBuilder* parent_builder)
: parent_(parent_builder),
byte_data_(),
children_(zone),
function_scope_(nullptr),
num_inner_functions_(0),
num_inner_with_data_(0),
bailed_out_(false),
has_data_(false) {}
void PreparseDataBuilder::DataGatheringScope::Start(
DeclarationScope* function_scope) {
PreparseDataBuilder* parent = preparser_->preparse_data_builder();
Zone* main_zone = preparser_->main_zone();
builder_ = new (main_zone) PreparseDataBuilder(main_zone, parent);
builder_ = new (main_zone)
PreparseDataBuilder(main_zone, preparser_->preparse_data_builder());
preparser_->set_preparse_data_builder(builder_);
function_scope->set_preparse_data_builder(builder_);
}
PreparseDataBuilder::DataGatheringScope::~DataGatheringScope() {
if (builder_ == nullptr) return;
// Copy over the data from the buffer into the zone-allocated byte_data_
PreparseDataBuilder* parent = builder_->parent_;
if (parent != nullptr && builder_->HasData()) {
parent->data_for_inner_functions_.push_back(builder_);
if (parent != nullptr && builder_->HasDataForParent()) {
parent->children_.push_back(builder_);
}
preparser_->set_preparse_data_builder(parent);
}
void PreparseDataBuilder::DataGatheringScope::AddSkippableFunction(
DeclarationScope* function_scope, int end_position,
int num_inner_functions) {
builder_->parent_->AddSkippableFunction(
function_scope->start_position(), end_position,
function_scope->num_parameters(), num_inner_functions,
function_scope->language_mode(), builder_->HasData(),
function_scope->NeedsHomeObject());
void PreparseDataBuilder::ByteData::WriteUint32(uint32_t data) {
DCHECK(!is_finalized_);
#ifdef DEBUG
// Save expected item size in debug mode.
byte_data_->push_back(kUint32Size);
#endif
byte_data_->push_back(data & 0xFF);
byte_data_->push_back((data >> 8) & 0xFF);
byte_data_->push_back((data >> 16) & 0xFF);
byte_data_->push_back((data >> 24) & 0xFF);
free_quarters_in_last_byte_ = 0;
}
void PreparseDataBuilder::AddSkippableFunction(
int start_position, int end_position, int num_parameters,
int num_inner_functions, LanguageMode language_mode, bool has_data,
bool uses_super_property) {
if (bailed_out_) return;
// Start position is used for a sanity check when consuming the data, we could
// remove it in the future if we're very pressed for space but it's been good
// at catching bugs in the wild so far.
byte_data_->WriteUint32(start_position);
byte_data_->WriteUint32(end_position);
uint32_t has_data_and_num_parameters =
HasDataField::encode(has_data) |
NumberOfParametersField::encode(num_parameters);
byte_data_->WriteUint32(has_data_and_num_parameters);
byte_data_->WriteUint32(num_inner_functions);
uint8_t language_and_super = LanguageField::encode(language_mode) |
UsesSuperField::encode(uses_super_property);
byte_data_->WriteQuarter(language_and_super);
}
void PreparseDataBuilder::SaveScopeAllocationData(DeclarationScope* scope) {
// The data contains a uint32 (reserved space for scope_data_start) and
// function data items, kSkippableFunctionDataSize each.
DCHECK_GE(byte_data_->size(), ByteData::kPlaceholderSize);
DCHECK_LE(byte_data_->size(), std::numeric_limits<uint32_t>::max());
DCHECK_EQ(byte_data_->size() % ByteData::kSkippableFunctionDataSize,
ByteData::kPlaceholderSize);
if (bailed_out_) return;
// If there are no skippable inner functions, we don't need to save anything.
if (!ContainsInnerFunctions()) return;
#ifdef DEBUG
uint32_t scope_data_start = static_cast<uint32_t>(byte_data_->size());
byte_data_->OverwriteFirstUint32(scope_data_start);
void PreparseDataBuilder::ByteData::SaveCurrentSizeAtFirstUint32() {
CHECK(!is_finalized_);
uint32_t data = static_cast<uint32_t>(byte_data_->size());
uint8_t* start = &byte_data_->front();
int i = 0;
// Check that that position already holds an item of the expected size.
CHECK_GE(byte_data_->size(), kUint32Size);
CHECK_EQ(start[i++], kUint32Size);
start[i++] = data & 0xFF;
start[i++] = (data >> 8) & 0xFF;
start[i++] = (data >> 16) & 0xFF;
start[i++] = (data >> 24) & 0xFF;
}
// For a data integrity check, write a value between data about skipped inner
// funcs and data about variables.
byte_data_->WriteUint32(ByteData::kMagicValue);
byte_data_->WriteUint32(scope->start_position());
byte_data_->WriteUint32(scope->end_position());
int PreparseDataBuilder::ByteData::length() const {
CHECK(!is_finalized_);
return static_cast<int>(byte_data_->size());
}
#endif
if (ScopeNeedsData(scope)) SaveDataForScope(scope);
void PreparseDataBuilder::ByteData::WriteUint8(uint8_t data) {
DCHECK(!is_finalized_);
#ifdef DEBUG
// Save expected item size in debug mode.
byte_data_->push_back(kUint8Size);
#endif
byte_data_->push_back(data);
free_quarters_in_last_byte_ = 0;
}
bool PreparseDataBuilder::ContainsInnerFunctions() const {
return byte_data_->size() > ByteData::kPlaceholderSize;
}
bool PreparseDataBuilder::HasData() const {
return !bailed_out_ && ContainsInnerFunctions();
}
Handle<PreparseData> PreparseDataBuilder::Serialize(Isolate* isolate) {
DCHECK(HasData());
DCHECK(!ThisOrParentBailedOut());
int child_data_length = static_cast<int>(data_for_inner_functions_.size());
Handle<PreparseData> data = isolate->factory()->NewPreparseData(
static_cast<int>(byte_data_->size()), child_data_length);
byte_data_->StoreInto(*data);
int i = 0;
for (const auto& item : data_for_inner_functions_) {
DCHECK_NOT_NULL(item);
Handle<PreparseData> child_data = item->Serialize(isolate);
data->set_child(i++, *child_data);
void PreparseDataBuilder::ByteData::WriteQuarter(uint8_t data) {
DCHECK(!is_finalized_);
DCHECK_LE(data, 3);
if (free_quarters_in_last_byte_ == 0) {
#ifdef DEBUG
// Save a marker in debug mode.
byte_data_->push_back(kQuarterMarker);
#endif
byte_data_->push_back(0);
free_quarters_in_last_byte_ = 3;
} else {
--free_quarters_in_last_byte_;
}
return data;
uint8_t shift_amount = free_quarters_in_last_byte_ * 2;
DCHECK_EQ(byte_data_->back() & (3 << shift_amount), 0);
byte_data_->back() |= (data << shift_amount);
}
ZonePreparseData* PreparseDataBuilder::Serialize(Zone* zone) {
DCHECK(HasData());
DCHECK(!ThisOrParentBailedOut());
void PreparseDataBuilder::ByteData::Start(std::vector<uint8_t>* buffer) {
DCHECK(!is_finalized_);
byte_data_ = buffer;
DCHECK_EQ(byte_data_->size(), 0);
}
int child_length = static_cast<int>(data_for_inner_functions_.size());
ZonePreparseData* result =
new (zone) ZonePreparseData(zone, byte_data_, child_length);
void PreparseDataBuilder::ByteData::Finalize(Zone* zone) {
int size = static_cast<int>(byte_data_->size());
uint8_t* raw_zone_data =
static_cast<uint8_t*>(ZoneAllocationPolicy(zone).New(size));
memcpy(raw_zone_data, &byte_data_->front(), size);
int i = 0;
for (const auto& item : data_for_inner_functions_) {
DCHECK_NOT_NULL(item);
ZonePreparseData* child = item->Serialize(zone);
result->set_child(i++, child);
}
byte_data_->resize(0);
return result;
zone_byte_data_ = Vector<uint8_t>(raw_zone_data, size);
#ifdef DEBUG
is_finalized_ = true;
#endif
}
void PreparseDataBuilder::DataGatheringScope::SetSkippableFunction(
DeclarationScope* function_scope, int num_inner_functions) {
DCHECK_NULL(builder_->function_scope_);
builder_->function_scope_ = function_scope;
DCHECK_EQ(builder_->num_inner_functions_, 0);
builder_->num_inner_functions_ = num_inner_functions;
builder_->parent_->has_data_ = true;
}
bool PreparseDataBuilder::HasInnerFunctions() const {
return !children_.is_empty();
}
bool PreparseDataBuilder::HasData() const { return !bailed_out_ && has_data_; }
bool PreparseDataBuilder::HasDataForParent() const {
return HasData() || function_scope_ != nullptr;
}
bool PreparseDataBuilder::ScopeNeedsData(Scope* scope) {
@ -295,16 +233,64 @@ bool PreparseDataBuilder::ScopeNeedsData(Scope* scope) {
return false;
}
bool PreparseDataBuilder::ScopeIsSkippableFunctionScope(Scope* scope) {
// Lazy non-arrow function scopes are skippable. Lazy functions are exactly
// those Scopes which have their own PreparseDataBuilder object. This
// logic ensures that the scope allocation data is consistent with the
// skippable function data (both agree on where the lazy function boundaries
// are).
if (scope->scope_type() != ScopeType::FUNCTION_SCOPE) return false;
DeclarationScope* declaration_scope = scope->AsDeclarationScope();
return !declaration_scope->is_arrow_scope() &&
declaration_scope->preparse_data_builder() != nullptr;
bool PreparseDataBuilder::SaveDataForSkippableFunction(
PreparseDataBuilder* builder) {
if (builder->bailed_out_) return false;
DeclarationScope* function_scope = builder->function_scope_;
// Start position is used for a sanity check when consuming the data, we could
// remove it in the future if we're very pressed for space but it's been good
// at catching bugs in the wild so far.
byte_data_.WriteUint32(function_scope->start_position());
byte_data_.WriteUint32(function_scope->end_position());
bool has_data = builder->has_data_;
uint32_t has_data_and_num_parameters =
HasDataField::encode(has_data) |
NumberOfParametersField::encode(function_scope->num_parameters());
byte_data_.WriteUint32(has_data_and_num_parameters);
byte_data_.WriteUint32(builder->num_inner_functions_);
uint8_t language_and_super =
LanguageField::encode(function_scope->language_mode()) |
UsesSuperField::encode(function_scope->NeedsHomeObject());
byte_data_.WriteQuarter(language_and_super);
return has_data;
}
void PreparseDataBuilder::SaveScopeAllocationData(DeclarationScope* scope,
Parser* parser) {
if (!HasData()) return;
DCHECK(HasInnerFunctions());
byte_data_.Start(parser->preparse_data_buffer());
#ifdef DEBUG
// Reserve Uint32 for scope_data_start debug info.
byte_data_.WriteUint32(0);
#endif
for (const auto& builder : children_) {
// Keep track of functions with inner data. {children_} contains also the
// builders that have no inner functions at all.
if (SaveDataForSkippableFunction(builder)) num_inner_with_data_++;
}
#ifdef DEBUG
// function data items, kSkippableFunctionDataSize each.
CHECK_GE(byte_data_.length(), kPlaceholderSize);
CHECK_LE(byte_data_.length(), std::numeric_limits<uint32_t>::max());
CHECK_EQ(byte_data_.length() % kSkippableFunctionDataSize, kPlaceholderSize);
byte_data_.SaveCurrentSizeAtFirstUint32();
// For a data integrity check, write a value between data about skipped inner
// funcs and data about variables.
byte_data_.WriteUint32(kMagicValue);
byte_data_.WriteUint32(scope->start_position());
byte_data_.WriteUint32(scope->end_position());
#endif
if (ScopeNeedsData(scope)) SaveDataForScope(scope);
byte_data_.Finalize(parser->factory()->zone());
}
void PreparseDataBuilder::SaveDataForScope(Scope* scope) {
@ -312,7 +298,7 @@ void PreparseDataBuilder::SaveDataForScope(Scope* scope) {
DCHECK(ScopeNeedsData(scope));
#ifdef DEBUG
byte_data_->WriteUint8(scope->scope_type());
byte_data_.WriteUint8(scope->scope_type());
#endif
uint8_t eval =
@ -320,7 +306,7 @@ void PreparseDataBuilder::SaveDataForScope(Scope* scope) {
scope->is_declaration_scope() &&
scope->AsDeclarationScope()->calls_sloppy_eval()) |
InnerScopeCallsEvalField::encode(scope->inner_scope_calls_eval());
byte_data_->WriteUint8(eval);
byte_data_.WriteUint8(eval);
if (scope->scope_type() == ScopeType::FUNCTION_SCOPE) {
Variable* function = scope->AsDeclarationScope()->function_var();
@ -339,17 +325,18 @@ void PreparseDataBuilder::SaveDataForVariable(Variable* var) {
// Store the variable name in debug mode; this way we can check that we
// restore data to the correct variable.
const AstRawString* name = var->raw_name();
byte_data_->WriteUint8(name->is_one_byte());
byte_data_->WriteUint32(name->length());
byte_data_.WriteUint8(name->is_one_byte());
byte_data_.WriteUint32(name->length());
for (int i = 0; i < name->length(); ++i) {
byte_data_->WriteUint8(name->raw_data()[i]);
byte_data_.WriteUint8(name->raw_data()[i]);
}
#endif
byte variable_data = VariableMaybeAssignedField::encode(
var->maybe_assigned() == kMaybeAssigned) |
VariableContextAllocatedField::encode(
var->has_forced_context_allocation());
byte_data_->WriteQuarter(variable_data);
byte_data_.WriteQuarter(variable_data);
}
void PreparseDataBuilder::SaveDataForInnerScopes(Scope* scope) {
@ -369,6 +356,63 @@ void PreparseDataBuilder::SaveDataForInnerScopes(Scope* scope) {
}
}
bool PreparseDataBuilder::ScopeIsSkippableFunctionScope(Scope* scope) {
// Lazy non-arrow function scopes are skippable. Lazy functions are exactly
// those Scopes which have their own PreparseDataBuilder object. This
// logic ensures that the scope allocation data is consistent with the
// skippable function data (both agree on where the lazy function boundaries
// are).
if (scope->scope_type() != ScopeType::FUNCTION_SCOPE) return false;
DeclarationScope* declaration_scope = scope->AsDeclarationScope();
return !declaration_scope->is_arrow_scope() &&
declaration_scope->preparse_data_builder() != nullptr;
}
Handle<PreparseData> PreparseDataBuilder::ByteData::CopyToHeap(
Isolate* isolate, int children_length) {
DCHECK(is_finalized_);
int data_length = zone_byte_data_.length();
Handle<PreparseData> data =
isolate->factory()->NewPreparseData(data_length, children_length);
data->copy_in(0, zone_byte_data_.start(), data_length);
return data;
}
ZonePreparseData* PreparseDataBuilder::ByteData::CopyToZone(
Zone* zone, int children_length) {
DCHECK(is_finalized_);
return new (zone) ZonePreparseData(zone, &zone_byte_data_, children_length);
}
Handle<PreparseData> PreparseDataBuilder::Serialize(Isolate* isolate) {
DCHECK(HasData());
DCHECK(!ThisOrParentBailedOut());
Handle<PreparseData> data =
byte_data_.CopyToHeap(isolate, num_inner_with_data_);
int i = 0;
for (const auto& builder : children_) {
if (!builder->HasData()) continue;
Handle<PreparseData> child_data = builder->Serialize(isolate);
data->set_child(i++, *child_data);
}
DCHECK_EQ(i, data->children_length());
return data;
}
ZonePreparseData* PreparseDataBuilder::Serialize(Zone* zone) {
DCHECK(HasData());
DCHECK(!ThisOrParentBailedOut());
ZonePreparseData* data = byte_data_.CopyToZone(zone, num_inner_with_data_);
int i = 0;
for (const auto& builder : children_) {
if (!builder->HasData()) continue;
ZonePreparseData* child = builder->Serialize(zone);
data->set_child(i++, child);
}
DCHECK_EQ(i, data->children_length());
return data;
}
class BuilderProducedPreparseData final : public ProducedPreparseData {
public:
explicit BuilderProducedPreparseData(PreparseDataBuilder* builder)
@ -571,6 +615,7 @@ void BaseConsumedPreparseData<Data>::RestoreDataForInnerScopes(Scope* scope) {
template <class Data>
void BaseConsumedPreparseData<Data>::VerifyDataStart() {
typename ByteData::ReadingScope reading_scope(this);
// The first uint32 contains the size of the skippable function data.
int scope_data_start = scope_data_->ReadUint32();
scope_data_->SetPosition(scope_data_start);
DCHECK_EQ(scope_data_->ReadUint32(), ByteData::kMagicValue);
@ -598,15 +643,14 @@ OnHeapConsumedPreparseData::OnHeapConsumedPreparseData(
#endif
}
ZonePreparseData::ZonePreparseData(Zone* zone,
PreparseDataBuilder::ByteData* byte_data,
int child_length)
ZonePreparseData::ZonePreparseData(Zone* zone, Vector<uint8_t>* byte_data,
int children_length)
: byte_data_(byte_data->begin(), byte_data->end(), zone),
children_(child_length, zone) {}
children_(children_length, zone) {}
Handle<PreparseData> ZonePreparseData::Serialize(Isolate* isolate) {
int data_size = static_cast<int>(byte_data()->size());
int child_data_length = child_length();
int child_data_length = children_length();
Handle<PreparseData> result =
isolate->factory()->NewPreparseData(data_size, child_data_length);
result->copy_in(0, byte_data()->data(), data_size);
@ -634,7 +678,7 @@ ZoneVectorWrapper ZoneConsumedPreparseData::GetScopeData() {
ProducedPreparseData* ZoneConsumedPreparseData::GetChildData(Zone* zone,
int child_index) {
CHECK_GT(data_->child_length(), child_index);
CHECK_GT(data_->children_length(), child_index);
ZonePreparseData* child_data = data_->get_child(child_index);
if (child_data == nullptr) return nullptr;
return ProducedPreparseData::For(child_data, zone);

View File

@ -17,6 +17,7 @@ namespace internal {
template <typename T>
class PodArray;
class Parser;
class PreParser;
class PreparseData;
class ZonePreparseData;
@ -62,13 +63,30 @@ class ZonePreparseData;
*/
class PreparseDataBuilder : public ZoneObject {
public:
class ByteData;
struct PreparseByteDataConstants {
#ifdef DEBUG
static constexpr int kMagicValue = 0xC0DE0DE;
static constexpr size_t kUint32Size = 5;
static constexpr size_t kUint8Size = 2;
static constexpr size_t kQuarterMarker = 0;
static constexpr size_t kPlaceholderSize = kUint32Size;
#else
static constexpr size_t kUint32Size = 4;
static constexpr size_t kUint8Size = 1;
static constexpr size_t kPlaceholderSize = 0;
#endif
static const size_t kSkippableFunctionDataSize =
4 * kUint32Size + 1 * kUint8Size;
};
class PreparseDataBuilder : public ZoneObject,
public PreparseByteDataConstants {
public:
// Create a PreparseDataBuilder object which will collect data as we
// parse.
PreparseDataBuilder(Zone* zone, PreparseDataBuilder* parent);
explicit PreparseDataBuilder(Zone* zone, PreparseDataBuilder* parent_builder);
PreparseDataBuilder* parent() const { return parent_; }
@ -81,8 +99,8 @@ class PreparseDataBuilder : public ZoneObject {
: preparser_(preparser), builder_(nullptr) {}
void Start(DeclarationScope* function_scope);
void AddSkippableFunction(DeclarationScope* function_scope,
int end_position, int num_inner_functions);
void SetSkippableFunction(DeclarationScope* function_scope,
int num_inner_functions);
~DataGatheringScope();
private:
@ -92,9 +110,46 @@ class PreparseDataBuilder : public ZoneObject {
DISALLOW_COPY_AND_ASSIGN(DataGatheringScope);
};
class ByteData : public ZoneObject, public PreparseByteDataConstants {
public:
ByteData() : byte_data_(nullptr), free_quarters_in_last_byte_(0) {}
~ByteData() {}
void Start(std::vector<uint8_t>* buffer);
void Finalize(Zone* zone);
Handle<PreparseData> CopyToHeap(Isolate* isolate, int children_length);
ZonePreparseData* CopyToZone(Zone* zone, int children_length);
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 SaveCurrentSizeAtFirstUint32();
int length() const;
#endif
private:
union {
// Only used during construction (is_finalized_ == false).
std::vector<uint8_t>* byte_data_;
// 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
};
// Saves the information needed for allocating the Scope's (and its
// subscopes') variables.
void SaveScopeAllocationData(DeclarationScope* scope);
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
@ -116,8 +171,9 @@ class PreparseDataBuilder : public ZoneObject {
}
#endif // DEBUG
bool ContainsInnerFunctions() const;
bool HasInnerFunctions() const;
bool HasData() const;
bool HasDataForParent() const;
static bool ScopeNeedsData(Scope* scope);
static bool ScopeIsSkippableFunctionScope(Scope* scope);
@ -135,14 +191,21 @@ class PreparseDataBuilder : public ZoneObject {
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_;
ZoneChunkList<PreparseDataBuilder*> children_;
ByteData* byte_data_;
ZoneChunkList<PreparseDataBuilder*> data_for_inner_functions_;
DeclarationScope* function_scope_;
int num_inner_functions_;
int num_inner_with_data_;
// Whether we've given up producing the data for this function.
bool bailed_out_;
bool bailed_out_ : 1;
bool has_data_ : 1;
DISALLOW_COPY_AND_ASSIGN(PreparseDataBuilder);
};

View File

@ -342,8 +342,8 @@ PreParser::Expression PreParser::ParseFunctionLiteral(
CheckStrictOctalLiteral(start_position, end_position());
}
if (skippable_function) {
preparse_data_builder_scope.AddSkippableFunction(
function_scope, end_position(), GetLastFunctionLiteralId() - func_id);
preparse_data_builder_scope.SetSkippableFunction(
function_scope, GetLastFunctionLiteralId() - func_id);
}
}

View File

@ -801,7 +801,9 @@ TEST(ProducingAndConsumingByteData) {
LocalContext env;
i::Zone zone(isolate->allocator(), ZONE_NAME);
i::PreparseDataBuilder::ByteData bytes(&zone);
std::vector<uint8_t> buffer;
i::PreparseDataBuilder::ByteData bytes;
bytes.Start(&buffer);
// Write some data.
bytes.WriteUint32(1983); // This will be overwritten.
bytes.WriteUint32(2147483647);
@ -810,7 +812,9 @@ TEST(ProducingAndConsumingByteData) {
bytes.WriteUint32(0);
bytes.WriteUint8(0);
#ifdef DEBUG
bytes.OverwriteFirstUint32(2017);
bytes.SaveCurrentSizeAtFirstUint32();
int saved_size = 21;
CHECK_EQ(buffer.size(), saved_size);
#endif
bytes.WriteUint8(100);
// Write quarter bytes between uint8s and uint32s to verify they're stored
@ -828,16 +832,35 @@ TEST(ProducingAndConsumingByteData) {
// End with a lonely quarter.
bytes.WriteQuarter(2);
#ifdef DEBUG
CHECK_EQ(buffer.size(), 38);
#else
CHECK_EQ(buffer.size(), 25);
#endif
// Copy buffer for sanity checks later-on.
std::vector<uint8_t> copied_buffer(buffer);
// Move the data from the temporary buffer into the zone for later
// serialization.
bytes.Finalize(&zone);
CHECK_EQ(buffer.size(), 0);
CHECK_LT(0, copied_buffer.size());
{
// Serialize as a ZoneConsumedPreparseData, and read back data.
i::ZonePreparseData zone_serialized(&zone, &bytes, 0);
i::ZonePreparseData* data_in_zone = bytes.CopyToZone(&zone, 0);
i::ZoneConsumedPreparseData::ByteData bytes_for_reading;
i::ZoneVectorWrapper wrapper(zone_serialized.byte_data());
i::ZoneVectorWrapper wrapper(data_in_zone->byte_data());
i::ZoneConsumedPreparseData::ByteData::ReadingScope reading_scope(
&bytes_for_reading, wrapper);
for (int i = 0; i < static_cast<int>(copied_buffer.size()); i++) {
CHECK_EQ(copied_buffer.at(i), wrapper.get(i));
}
#ifdef DEBUG
CHECK_EQ(bytes_for_reading.ReadUint32(), 2017);
CHECK_EQ(bytes_for_reading.ReadUint32(), saved_size);
#else
CHECK_EQ(bytes_for_reading.ReadUint32(), 1983);
#endif
@ -858,19 +881,25 @@ TEST(ProducingAndConsumingByteData) {
CHECK_EQ(bytes_for_reading.ReadQuarter(), 2);
CHECK_EQ(bytes_for_reading.ReadUint32(), 50);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 2);
// We should have consumed all data at this point.
CHECK(!bytes_for_reading.HasRemainingBytes(1));
}
{
// Serialize as an OnHeapConsumedPreparseData, and read back data.
i::Handle<i::PreparseData> data_on_heap =
isolate->factory()->NewPreparseData(static_cast<int>(bytes.size()), 0);
bytes.StoreInto(*data_on_heap);
i::Handle<i::PreparseData> data_on_heap = bytes.CopyToHeap(isolate, 0);
CHECK_EQ(copied_buffer.size(), data_on_heap->data_length());
CHECK_EQ(data_on_heap->children_length(), 0);
i::OnHeapConsumedPreparseData::ByteData bytes_for_reading;
i::OnHeapConsumedPreparseData::ByteData::ReadingScope reading_scope(
&bytes_for_reading, *data_on_heap);
for (int i = 0; i < static_cast<int>(copied_buffer.size()); i++) {
CHECK_EQ(copied_buffer[i], data_on_heap->get(i));
}
#ifdef DEBUG
CHECK_EQ(bytes_for_reading.ReadUint32(), 2017);
CHECK_EQ(bytes_for_reading.ReadUint32(), saved_size);
#else
CHECK_EQ(bytes_for_reading.ReadUint32(), 1983);
#endif
@ -891,5 +920,7 @@ TEST(ProducingAndConsumingByteData) {
CHECK_EQ(bytes_for_reading.ReadQuarter(), 2);
CHECK_EQ(bytes_for_reading.ReadUint32(), 50);
CHECK_EQ(bytes_for_reading.ReadQuarter(), 2);
// We should have consumed all data at this point.
CHECK(!bytes_for_reading.HasRemainingBytes(1));
}
}