[parser] Skipping inner funcs: Make the data on heap smaller.
We were unnecessarily storing everything as uint32_t, even though many items in the preparsed scope data can be stored as uint8_t. This CL also adds an (internal) API which abstracts away the actual data storing, so the backing store can be made even more efficient (e.g., use only 1-3 bytes for some uint32_t values, if they fit) without affecting other parts of the code. BUG=v8:5516,chromium:762492 Change-Id: I7cd4d91dc11f87f8aec9c7584044a6f2a59b73ba Reviewed-on: https://chromium-review.googlesource.com/684182 Commit-Queue: Marja Hölttä <marja@chromium.org> Reviewed-by: Camillo Bruni <cbruni@chromium.org> Cr-Commit-Position: refs/heads/master@{#48231}
This commit is contained in:
parent
d2e22dbf6d
commit
a02580636f
@ -1743,7 +1743,7 @@ Handle<ModuleInfo> Factory::NewModuleInfo() {
|
||||
Handle<PreParsedScopeData> Factory::NewPreParsedScopeData() {
|
||||
Handle<PreParsedScopeData> result =
|
||||
Handle<PreParsedScopeData>::cast(NewStruct(TUPLE2_TYPE, TENURED));
|
||||
result->set_scope_data(PodArray<uint32_t>::cast(*empty_byte_array()));
|
||||
result->set_scope_data(PodArray<uint8_t>::cast(*empty_byte_array()));
|
||||
result->set_child_data(*empty_fixed_array());
|
||||
return result;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
CAST_ACCESSOR(PreParsedScopeData)
|
||||
ACCESSORS(PreParsedScopeData, scope_data, PodArray<uint32_t>, kScopeDataOffset)
|
||||
ACCESSORS(PreParsedScopeData, scope_data, PodArray<uint8_t>, kScopeDataOffset)
|
||||
ACCESSORS(PreParsedScopeData, child_data, FixedArray, kChildDataOffset)
|
||||
|
||||
TYPE_CHECKER(SharedFunctionInfo, SHARED_FUNCTION_INFO_TYPE)
|
||||
|
@ -19,7 +19,7 @@ class DebugInfo;
|
||||
|
||||
class PreParsedScopeData : public Struct {
|
||||
public:
|
||||
DECL_ACCESSORS(scope_data, PodArray<uint32_t>)
|
||||
DECL_ACCESSORS(scope_data, PodArray<uint8_t>)
|
||||
DECL_ACCESSORS(child_data, FixedArray)
|
||||
|
||||
static const int kScopeDataOffset = Struct::kHeaderSize;
|
||||
|
@ -28,14 +28,15 @@ class VariableContextAllocatedField
|
||||
|
||||
const int kMagicValue = 0xc0de0de;
|
||||
|
||||
enum SkippableFunctionDataOffsets {
|
||||
kStartPosition,
|
||||
kEndPosition,
|
||||
kNumParameters,
|
||||
kNumInnerFunctions,
|
||||
kLanguageAndSuper,
|
||||
kSize
|
||||
};
|
||||
#ifdef DEBUG
|
||||
const size_t kUint32Size = 5;
|
||||
const size_t kUint8Size = 2;
|
||||
#else
|
||||
const size_t kUint32Size = 4;
|
||||
const size_t kUint8Size = 1;
|
||||
#endif
|
||||
|
||||
const int kSkippableFunctionDataSize = 4 * kUint32Size + 1 * kUint8Size;
|
||||
|
||||
STATIC_ASSERT(LANGUAGE_END == 2);
|
||||
class LanguageField : public BitField<int, 0, 1> {};
|
||||
@ -45,17 +46,20 @@ class UsesSuperField : public BitField<bool, LanguageField::kNext, 1> {};
|
||||
|
||||
/*
|
||||
|
||||
Internal data format for the backing store of ProducedPreparsedScopeData:
|
||||
Internal data format for the backing store of ProducedPreparsedScopeData and
|
||||
PreParsedScopeData::scope_data (on the heap):
|
||||
|
||||
(Skippable function data:)
|
||||
------------------------------------
|
||||
| scope_data_start |
|
||||
------------------------------------
|
||||
| data for inner function 1 |
|
||||
| ... |
|
||||
------------------------------------
|
||||
| data for inner function n |
|
||||
| ... |
|
||||
------------------------------------
|
||||
(Scope allocation data:)
|
||||
(Scope allocation data:) << scope_data_start points here
|
||||
------------------------------------
|
||||
magic value
|
||||
------------------------------------
|
||||
@ -78,32 +82,84 @@ class UsesSuperField : public BitField<bool, LanguageField::kNext, 1> {};
|
||||
| ... |
|
||||
------------------------------------
|
||||
|
||||
|
||||
Data format for PreParsedScopeData (on the heap):
|
||||
|
||||
PreParsedScopeData::scope_data:
|
||||
|
||||
------------------------------------
|
||||
| scope_data_start |
|
||||
------------------------------------
|
||||
| Skippable function data |
|
||||
| (see above) |
|
||||
| ... |
|
||||
------------------------------------
|
||||
------------------------------------
|
||||
| Scope allocation data | << scope_data_start points here
|
||||
| (see above) |
|
||||
| ... |
|
||||
------------------------------------
|
||||
|
||||
PreParsedScopeData::child_data is an array of PreParsedScopeData objects, one
|
||||
for each skippable inner function.
|
||||
|
||||
|
||||
ConsumedPreParsedScopeData wraps a PreParsedScopeData and reads data from it.
|
||||
|
||||
*/
|
||||
|
||||
void ProducedPreParsedScopeData::ByteData::WriteUint32(uint32_t data) {
|
||||
#ifdef DEBUG
|
||||
// Save expected item size in debug mode.
|
||||
backing_store_.push_back(kUint32Size);
|
||||
#endif
|
||||
const uint8_t* d = reinterpret_cast<uint8_t*>(&data);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
backing_store_.push_back(*d++);
|
||||
}
|
||||
}
|
||||
|
||||
void ProducedPreParsedScopeData::ByteData::OverwriteFirstUint32(uint32_t data) {
|
||||
size_t position = 0;
|
||||
#ifdef DEBUG
|
||||
// Check that that position already holds an item of the expected size.
|
||||
DCHECK_GE(backing_store_.size(), kUint32Size);
|
||||
DCHECK_EQ(backing_store_[0], kUint32Size);
|
||||
++position;
|
||||
#endif
|
||||
const uint8_t* d = reinterpret_cast<uint8_t*>(&data);
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
backing_store_[position + i] = *d++;
|
||||
}
|
||||
}
|
||||
|
||||
void ProducedPreParsedScopeData::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);
|
||||
}
|
||||
|
||||
Handle<PodArray<uint8_t>> ProducedPreParsedScopeData::ByteData::Serialize(
|
||||
Isolate* isolate) const {
|
||||
Handle<PodArray<uint8_t>> array = PodArray<uint8_t>::New(
|
||||
isolate, static_cast<int>(backing_store_.size()), TENURED);
|
||||
|
||||
DisallowHeapAllocation no_gc;
|
||||
PodArray<uint8_t>* raw_array = *array;
|
||||
|
||||
int i = 0;
|
||||
for (uint8_t item : backing_store_) {
|
||||
raw_array->set(i++, item);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
ProducedPreParsedScopeData::ProducedPreParsedScopeData(
|
||||
Zone* zone, ProducedPreParsedScopeData* parent)
|
||||
: parent_(parent),
|
||||
byte_data_(new (zone) ByteData(zone)),
|
||||
data_for_inner_functions_(zone),
|
||||
bailed_out_(false) {
|
||||
if (parent != nullptr) {
|
||||
parent->data_for_inner_functions_.push_back(this);
|
||||
}
|
||||
// Reserve space for scope_data_start, written later:
|
||||
byte_data_->WriteUint32(0);
|
||||
}
|
||||
|
||||
// Create a ProducedPreParsedScopeData which is just a proxy for a previous
|
||||
// produced PreParsedScopeData.
|
||||
ProducedPreParsedScopeData::ProducedPreParsedScopeData(
|
||||
Handle<PreParsedScopeData> data, Zone* zone)
|
||||
: parent_(nullptr),
|
||||
byte_data_(nullptr),
|
||||
data_for_inner_functions_(zone),
|
||||
bailed_out_(false),
|
||||
previously_produced_preparsed_scope_data_(data) {}
|
||||
|
||||
ProducedPreParsedScopeData::DataGatheringScope::DataGatheringScope(
|
||||
DeclarationScope* function_scope, PreParser* preparser)
|
||||
: function_scope_(function_scope),
|
||||
@ -145,58 +201,52 @@ void ProducedPreParsedScopeData::AddSkippableFunction(
|
||||
int num_inner_functions, LanguageMode language_mode,
|
||||
bool uses_super_property) {
|
||||
DCHECK(FLAG_preparser_scope_analysis);
|
||||
DCHECK_EQ(scope_data_start_, -1);
|
||||
DCHECK(previously_produced_preparsed_scope_data_.is_null());
|
||||
|
||||
if (bailed_out_) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t current_size = backing_store_.size();
|
||||
backing_store_.resize(current_size + SkippableFunctionDataOffsets::kSize);
|
||||
backing_store_[current_size + SkippableFunctionDataOffsets::kStartPosition] =
|
||||
start_position;
|
||||
backing_store_[current_size + SkippableFunctionDataOffsets::kEndPosition] =
|
||||
end_position;
|
||||
backing_store_[current_size + SkippableFunctionDataOffsets::kNumParameters] =
|
||||
num_parameters;
|
||||
backing_store_[current_size +
|
||||
SkippableFunctionDataOffsets::kNumInnerFunctions] =
|
||||
num_inner_functions;
|
||||
byte_data_->WriteUint32(start_position);
|
||||
byte_data_->WriteUint32(end_position);
|
||||
byte_data_->WriteUint32(num_parameters);
|
||||
byte_data_->WriteUint32(num_inner_functions);
|
||||
|
||||
uint32_t language_and_super = LanguageField::encode(language_mode) |
|
||||
UsesSuperField::encode(uses_super_property);
|
||||
uint8_t language_and_super = LanguageField::encode(language_mode) |
|
||||
UsesSuperField::encode(uses_super_property);
|
||||
|
||||
backing_store_[current_size +
|
||||
SkippableFunctionDataOffsets::kLanguageAndSuper] =
|
||||
language_and_super;
|
||||
byte_data_->WriteUint8(language_and_super);
|
||||
}
|
||||
|
||||
void ProducedPreParsedScopeData::SaveScopeAllocationData(
|
||||
DeclarationScope* scope) {
|
||||
DCHECK(FLAG_preparser_scope_analysis);
|
||||
DCHECK(previously_produced_preparsed_scope_data_.is_null());
|
||||
DCHECK_EQ(scope_data_start_, -1);
|
||||
DCHECK_EQ(backing_store_.size() % SkippableFunctionDataOffsets::kSize, 0);
|
||||
// The data contains a uint32 (reserved space for scope_data_start) and
|
||||
// function data items, kSkippableFunctionDataSize each.
|
||||
DCHECK_GE(byte_data_->size(), kUint32Size);
|
||||
DCHECK_LE(byte_data_->size(), std::numeric_limits<uint32_t>::max());
|
||||
DCHECK_EQ(byte_data_->size() % kSkippableFunctionDataSize, kUint32Size);
|
||||
|
||||
if (bailed_out_) {
|
||||
return;
|
||||
}
|
||||
|
||||
scope_data_start_ = static_cast<int>(backing_store_.size());
|
||||
uint32_t scope_data_start = static_cast<uint32_t>(byte_data_->size());
|
||||
|
||||
// If there are no skippable inner functions, we don't need to save anything.
|
||||
if (backing_store_.size() == 0) {
|
||||
if (scope_data_start == kUint32Size) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For sanity checks.
|
||||
backing_store_.push_back(kMagicValue);
|
||||
backing_store_.push_back(scope->start_position());
|
||||
backing_store_.push_back(scope->end_position());
|
||||
byte_data_->OverwriteFirstUint32(scope_data_start);
|
||||
|
||||
// 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());
|
||||
|
||||
SaveDataForScope(scope);
|
||||
}
|
||||
|
||||
@ -204,7 +254,6 @@ MaybeHandle<PreParsedScopeData> ProducedPreParsedScopeData::Serialize(
|
||||
Isolate* isolate) const {
|
||||
if (!previously_produced_preparsed_scope_data_.is_null()) {
|
||||
DCHECK(!bailed_out_);
|
||||
DCHECK_EQ(backing_store_.size(), 0);
|
||||
DCHECK_EQ(data_for_inner_functions_.size(), 0);
|
||||
return previously_produced_preparsed_scope_data_;
|
||||
}
|
||||
@ -214,27 +263,16 @@ MaybeHandle<PreParsedScopeData> ProducedPreParsedScopeData::Serialize(
|
||||
|
||||
DCHECK(!ThisOrParentBailedOut());
|
||||
|
||||
// FIXME(marja): save space by using a byte array and converting
|
||||
// function data to bytes.
|
||||
size_t length = backing_store_.size();
|
||||
if (length == 0) {
|
||||
if (byte_data_->size() <= kUint32Size) {
|
||||
// The data contains only the placeholder.
|
||||
return MaybeHandle<PreParsedScopeData>();
|
||||
}
|
||||
|
||||
Handle<PodArray<uint32_t>> data_array =
|
||||
PodArray<uint32_t>::New(isolate, static_cast<int>(length + 1), TENURED);
|
||||
|
||||
DCHECK_GE(scope_data_start_, 0);
|
||||
data_array->set(0, scope_data_start_);
|
||||
{
|
||||
int i = 1;
|
||||
for (const auto& item : backing_store_) {
|
||||
data_array->set(i++, item);
|
||||
}
|
||||
}
|
||||
|
||||
Handle<PreParsedScopeData> data = isolate->factory()->NewPreParsedScopeData();
|
||||
|
||||
Handle<PodArray<uint8_t>> scope_data_array = byte_data_->Serialize(isolate);
|
||||
data->set_scope_data(*scope_data_array);
|
||||
|
||||
int child_data_length = static_cast<int>(data_for_inner_functions_.size());
|
||||
if (child_data_length == 0) {
|
||||
data->set_child_data(*(isolate->factory()->empty_fixed_array()));
|
||||
@ -256,7 +294,6 @@ MaybeHandle<PreParsedScopeData> ProducedPreParsedScopeData::Serialize(
|
||||
data->set_child_data(*child_array);
|
||||
}
|
||||
|
||||
data->set_scope_data(*data_array);
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -310,15 +347,15 @@ void ProducedPreParsedScopeData::SaveDataForScope(Scope* scope) {
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
backing_store_.push_back(scope->scope_type());
|
||||
byte_data_->WriteUint8(scope->scope_type());
|
||||
#endif
|
||||
|
||||
uint32_t eval =
|
||||
uint8_t eval =
|
||||
ScopeCallsSloppyEvalField::encode(
|
||||
scope->is_declaration_scope() &&
|
||||
scope->AsDeclarationScope()->calls_sloppy_eval()) |
|
||||
InnerScopeCallsEvalField::encode(scope->inner_scope_calls_eval());
|
||||
backing_store_.push_back(eval);
|
||||
byte_data_->WriteUint8(eval);
|
||||
|
||||
if (scope->scope_type() == ScopeType::FUNCTION_SCOPE) {
|
||||
Variable* function = scope->AsDeclarationScope()->function_var();
|
||||
@ -341,9 +378,9 @@ void ProducedPreParsedScopeData::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();
|
||||
backing_store_.push_back(name->length());
|
||||
byte_data_->WriteUint32(name->length());
|
||||
for (int i = 0; i < name->length(); ++i) {
|
||||
backing_store_.push_back(name->raw_data()[i]);
|
||||
byte_data_->WriteUint8(name->raw_data()[i]);
|
||||
}
|
||||
#endif
|
||||
// FIXME(marja): Only 3 bits needed, not a full byte.
|
||||
@ -353,7 +390,7 @@ void ProducedPreParsedScopeData::SaveDataForVariable(Variable* var) {
|
||||
VariableContextAllocatedField::encode(
|
||||
var->has_forced_context_allocation());
|
||||
|
||||
backing_store_.push_back(variable_data);
|
||||
byte_data_->WriteUint8(variable_data);
|
||||
}
|
||||
|
||||
void ProducedPreParsedScopeData::SaveDataForInnerScopes(Scope* scope) {
|
||||
@ -377,15 +414,51 @@ void ProducedPreParsedScopeData::SaveDataForInnerScopes(Scope* scope) {
|
||||
}
|
||||
}
|
||||
|
||||
ConsumedPreParsedScopeData::ByteData::ReadingScope::ReadingScope(
|
||||
ConsumedPreParsedScopeData* parent)
|
||||
: ReadingScope(parent->scope_data_.get(), parent->data_->scope_data()) {}
|
||||
|
||||
int32_t ConsumedPreParsedScopeData::ByteData::ReadUint32() {
|
||||
DCHECK_NOT_NULL(data_);
|
||||
DCHECK_GE(RemainingBytes(), kUint32Size);
|
||||
#ifdef DEBUG
|
||||
// Check that there indeed is an integer following.
|
||||
DCHECK_EQ(data_->get(index_++), kUint32Size);
|
||||
#endif
|
||||
int32_t result = 0;
|
||||
byte* p = reinterpret_cast<byte*>(&result);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
*p++ = data_->get(index_++);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t ConsumedPreParsedScopeData::ByteData::ReadUint8() {
|
||||
DCHECK_NOT_NULL(data_);
|
||||
DCHECK_GE(RemainingBytes(), kUint8Size);
|
||||
#ifdef DEBUG
|
||||
// Check that there indeed is a byte following.
|
||||
DCHECK_EQ(data_->get(index_++), kUint8Size);
|
||||
#endif
|
||||
return data_->get(index_++);
|
||||
}
|
||||
|
||||
ConsumedPreParsedScopeData::ConsumedPreParsedScopeData()
|
||||
: scope_data_(new ByteData()), child_index_(0) {}
|
||||
|
||||
ConsumedPreParsedScopeData::~ConsumedPreParsedScopeData() {}
|
||||
|
||||
void ConsumedPreParsedScopeData::SetData(Handle<PreParsedScopeData> data) {
|
||||
DCHECK(data->IsPreParsedScopeData());
|
||||
data_ = data;
|
||||
#ifdef DEBUG
|
||||
DisallowHeapAllocation no_gc;
|
||||
PodArray<uint32_t>* scope_data = data_->scope_data();
|
||||
DCHECK_GT(scope_data->length(), 2);
|
||||
DCHECK_EQ(scope_data->get(scope_data->get(0) + 1), kMagicValue);
|
||||
ByteData::ReadingScope reading_scope(this);
|
||||
int scope_data_start = scope_data_->ReadUint32();
|
||||
scope_data_->SetPosition(scope_data_start);
|
||||
DCHECK_EQ(scope_data_->ReadUint32(), kMagicValue);
|
||||
#endif
|
||||
// The first data item is scope_data_start. Skip over it.
|
||||
scope_data_->SetPosition(kUint32Size);
|
||||
}
|
||||
|
||||
ProducedPreParsedScopeData*
|
||||
@ -393,31 +466,22 @@ ConsumedPreParsedScopeData::GetDataForSkippableFunction(
|
||||
Zone* zone, int start_position, int* end_position, int* num_parameters,
|
||||
int* num_inner_functions, bool* uses_super_property,
|
||||
LanguageMode* language_mode) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
PodArray<uint32_t>* scope_data = data_->scope_data();
|
||||
|
||||
// The skippable function *must* be the next function in the data. Use the
|
||||
// start position as a sanity check.
|
||||
CHECK_GE(scope_data->length(), index_ + SkippableFunctionDataOffsets::kSize);
|
||||
int start_position_from_data =
|
||||
scope_data->get(index_ + SkippableFunctionDataOffsets::kStartPosition);
|
||||
ByteData::ReadingScope reading_scope(this);
|
||||
CHECK_GE(scope_data_->RemainingBytes(), kSkippableFunctionDataSize);
|
||||
int start_position_from_data = scope_data_->ReadUint32();
|
||||
CHECK_EQ(start_position, start_position_from_data);
|
||||
|
||||
*end_position =
|
||||
scope_data->get(index_ + SkippableFunctionDataOffsets::kEndPosition);
|
||||
*end_position = scope_data_->ReadUint32();
|
||||
DCHECK_GT(*end_position, start_position);
|
||||
*num_parameters =
|
||||
scope_data->get(index_ + SkippableFunctionDataOffsets::kNumParameters);
|
||||
*num_inner_functions = scope_data->get(
|
||||
index_ + SkippableFunctionDataOffsets::kNumInnerFunctions);
|
||||
*num_parameters = scope_data_->ReadUint32();
|
||||
*num_inner_functions = scope_data_->ReadUint32();
|
||||
|
||||
int language_and_super =
|
||||
scope_data->get(index_ + SkippableFunctionDataOffsets::kLanguageAndSuper);
|
||||
uint8_t language_and_super = scope_data_->ReadUint8();
|
||||
*language_mode = LanguageMode(LanguageField::decode(language_and_super));
|
||||
*uses_super_property = UsesSuperField::decode(language_and_super);
|
||||
|
||||
index_ += SkippableFunctionDataOffsets::kSize;
|
||||
|
||||
// Retrieve the corresponding PreParsedScopeData and associate it to the
|
||||
// skipped function. If the skipped functions contains inner functions, those
|
||||
// can be skipped when the skipped function is eagerly parsed.
|
||||
@ -438,34 +502,31 @@ void ConsumedPreParsedScopeData::RestoreScopeAllocationData(
|
||||
DCHECK_EQ(scope->scope_type(), ScopeType::FUNCTION_SCOPE);
|
||||
DCHECK(!data_.is_null());
|
||||
|
||||
DisallowHeapAllocation no_gc;
|
||||
PodArray<uint32_t>* scope_data = data_->scope_data();
|
||||
int magic_value_from_data = scope_data->get(index_++);
|
||||
ByteData::ReadingScope reading_scope(this);
|
||||
|
||||
int magic_value_from_data = scope_data_->ReadUint32();
|
||||
// Check that we've consumed all inner function data.
|
||||
CHECK_EQ(magic_value_from_data, kMagicValue);
|
||||
|
||||
int start_position_from_data = scope_data->get(index_++);
|
||||
int end_position_from_data = scope_data->get(index_++);
|
||||
int start_position_from_data = scope_data_->ReadUint32();
|
||||
int end_position_from_data = scope_data_->ReadUint32();
|
||||
CHECK_EQ(start_position_from_data, scope->start_position());
|
||||
CHECK_EQ(end_position_from_data, scope->end_position());
|
||||
|
||||
RestoreData(scope, scope_data);
|
||||
RestoreData(scope);
|
||||
|
||||
// Check that we consumed all scope data.
|
||||
DCHECK_EQ(index_, scope_data->length());
|
||||
DCHECK_EQ(scope_data_->RemainingBytes(), 0);
|
||||
}
|
||||
|
||||
void ConsumedPreParsedScopeData::SkipFunctionDataForTesting() {
|
||||
DCHECK_EQ(index_, 1);
|
||||
DisallowHeapAllocation no_gc;
|
||||
PodArray<uint32_t>* scope_data = data_->scope_data();
|
||||
DCHECK_GT(scope_data->length(), 2);
|
||||
index_ = scope_data->get(0) + 1;
|
||||
DCHECK_EQ(scope_data->get(index_), kMagicValue);
|
||||
ByteData::ReadingScope reading_scope(this);
|
||||
scope_data_->SetPosition(0);
|
||||
uint32_t scope_data_start = scope_data_->ReadUint32();
|
||||
scope_data_->SetPosition(scope_data_start);
|
||||
}
|
||||
|
||||
void ConsumedPreParsedScopeData::RestoreData(Scope* scope,
|
||||
PodArray<uint32_t>* scope_data) {
|
||||
void ConsumedPreParsedScopeData::RestoreData(Scope* scope) {
|
||||
if (scope->is_declaration_scope() &&
|
||||
scope->AsDeclarationScope()->is_skipped_function()) {
|
||||
return;
|
||||
@ -478,19 +539,18 @@ void ConsumedPreParsedScopeData::RestoreData(Scope* scope,
|
||||
return;
|
||||
}
|
||||
|
||||
if (scope_data->length() < index_ + 1) {
|
||||
if (scope_data_->RemainingBytes() < kUint8Size) {
|
||||
// Temporary debugging code for detecting inconsistent data. Write debug
|
||||
// information on the stack, then crash.
|
||||
scope_data->GetIsolate()->PushStackTraceAndDie(0xc0defee, nullptr, nullptr,
|
||||
0xc0defee);
|
||||
data_->GetIsolate()->PushStackTraceAndDie(0xc0defee, nullptr, nullptr,
|
||||
0xc0defee);
|
||||
}
|
||||
|
||||
// scope_type is stored only in debug mode.
|
||||
CHECK_GE(scope_data->length(), index_ + 1);
|
||||
DCHECK_GE(scope_data->length(), index_ + 2);
|
||||
DCHECK_EQ(scope_data->get(index_++), scope->scope_type());
|
||||
CHECK_GE(scope_data_->RemainingBytes(), kUint8Size);
|
||||
DCHECK_EQ(scope_data_->ReadUint8(), scope->scope_type());
|
||||
|
||||
uint32_t eval = scope_data->get(index_++);
|
||||
uint32_t eval = scope_data_->ReadUint8();
|
||||
if (ScopeCallsSloppyEvalField::decode(eval)) {
|
||||
scope->RecordEvalCall();
|
||||
}
|
||||
@ -501,31 +561,29 @@ void ConsumedPreParsedScopeData::RestoreData(Scope* scope,
|
||||
if (scope->scope_type() == ScopeType::FUNCTION_SCOPE) {
|
||||
Variable* function = scope->AsDeclarationScope()->function_var();
|
||||
if (function != nullptr) {
|
||||
RestoreDataForVariable(function, scope_data);
|
||||
RestoreDataForVariable(function);
|
||||
}
|
||||
}
|
||||
|
||||
for (Variable* var : *scope->locals()) {
|
||||
if (IsDeclaredVariableMode(var->mode())) {
|
||||
RestoreDataForVariable(var, scope_data);
|
||||
RestoreDataForVariable(var);
|
||||
}
|
||||
}
|
||||
|
||||
RestoreDataForInnerScopes(scope, scope_data);
|
||||
RestoreDataForInnerScopes(scope);
|
||||
}
|
||||
|
||||
void ConsumedPreParsedScopeData::RestoreDataForVariable(
|
||||
Variable* var, PodArray<uint32_t>* scope_data) {
|
||||
void ConsumedPreParsedScopeData::RestoreDataForVariable(Variable* var) {
|
||||
#ifdef DEBUG
|
||||
const AstRawString* name = var->raw_name();
|
||||
DCHECK_GT(scope_data->length(), index_ + name->length());
|
||||
DCHECK_EQ(scope_data->get(index_++), static_cast<uint32_t>(name->length()));
|
||||
DCHECK_EQ(scope_data_->ReadUint32(), static_cast<uint32_t>(name->length()));
|
||||
for (int i = 0; i < name->length(); ++i) {
|
||||
DCHECK_EQ(scope_data->get(index_++), name->raw_data()[i]);
|
||||
DCHECK_EQ(scope_data_->ReadUint8(), name->raw_data()[i]);
|
||||
}
|
||||
#endif
|
||||
CHECK_GT(scope_data->length(), index_);
|
||||
byte variable_data = scope_data->get(index_++);
|
||||
CHECK_GE(scope_data_->RemainingBytes(), kUint8Size);
|
||||
uint8_t variable_data = scope_data_->ReadUint8();
|
||||
if (VariableIsUsedField::decode(variable_data)) {
|
||||
var->set_is_used();
|
||||
}
|
||||
@ -537,15 +595,14 @@ void ConsumedPreParsedScopeData::RestoreDataForVariable(
|
||||
}
|
||||
}
|
||||
|
||||
void ConsumedPreParsedScopeData::RestoreDataForInnerScopes(
|
||||
Scope* scope, PodArray<uint32_t>* scope_data) {
|
||||
void ConsumedPreParsedScopeData::RestoreDataForInnerScopes(Scope* scope) {
|
||||
std::vector<Scope*> scopes;
|
||||
for (Scope* inner = scope->inner_scope(); inner != nullptr;
|
||||
inner = inner->sibling()) {
|
||||
scopes.push_back(inner);
|
||||
}
|
||||
for (auto it = scopes.rbegin(); it != scopes.rend(); ++it) {
|
||||
RestoreData(*it, scope_data);
|
||||
RestoreData(*it);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,29 +67,31 @@ class PreParsedScopeData;
|
||||
|
||||
class ProducedPreParsedScopeData : public ZoneObject {
|
||||
public:
|
||||
class ByteData : public ZoneObject {
|
||||
public:
|
||||
explicit ByteData(Zone* zone) : backing_store_(zone) {}
|
||||
|
||||
void WriteUint32(uint32_t data);
|
||||
void WriteUint8(uint8_t data);
|
||||
|
||||
// For overwriting previously written data at position 0.
|
||||
void OverwriteFirstUint32(uint32_t data);
|
||||
|
||||
Handle<PodArray<uint8_t>> Serialize(Isolate* isolate) const;
|
||||
|
||||
size_t size() const { return backing_store_.size(); }
|
||||
|
||||
private:
|
||||
ZoneDeque<uint8_t> backing_store_;
|
||||
};
|
||||
|
||||
// Create a ProducedPreParsedScopeData object which will collect data as we
|
||||
// parse.
|
||||
explicit ProducedPreParsedScopeData(Zone* zone,
|
||||
ProducedPreParsedScopeData* parent)
|
||||
: parent_(parent),
|
||||
backing_store_(zone),
|
||||
data_for_inner_functions_(zone),
|
||||
scope_data_start_(-1),
|
||||
bailed_out_(false) {
|
||||
if (parent != nullptr) {
|
||||
parent->data_for_inner_functions_.push_back(this);
|
||||
}
|
||||
}
|
||||
ProducedPreParsedScopeData(Zone* zone, ProducedPreParsedScopeData* parent);
|
||||
|
||||
// Create a ProducedPreParsedScopeData which is just a proxy for a previous
|
||||
// produced PreParsedScopeData.
|
||||
ProducedPreParsedScopeData(Handle<PreParsedScopeData> data, Zone* zone)
|
||||
: parent_(nullptr),
|
||||
backing_store_(zone),
|
||||
data_for_inner_functions_(zone),
|
||||
scope_data_start_(-1),
|
||||
bailed_out_(false),
|
||||
previously_produced_preparsed_scope_data_(data) {}
|
||||
ProducedPreParsedScopeData(Handle<PreParsedScopeData> data, Zone* zone);
|
||||
|
||||
ProducedPreParsedScopeData* parent() const { return parent_; }
|
||||
|
||||
@ -160,14 +162,8 @@ class ProducedPreParsedScopeData : public ZoneObject {
|
||||
|
||||
ProducedPreParsedScopeData* parent_;
|
||||
|
||||
// TODO(marja): Make the backing store more efficient once we know exactly
|
||||
// what data is needed.
|
||||
ZoneDeque<uint32_t> backing_store_;
|
||||
ByteData* byte_data_;
|
||||
ZoneDeque<ProducedPreParsedScopeData*> data_for_inner_functions_;
|
||||
// The backing store contains data about inner functions and then data about
|
||||
// this scope's (and its subscopes') variables. scope_data_start_ marks where
|
||||
// the latter starts.
|
||||
int scope_data_start_;
|
||||
|
||||
// Whether we've given up producing the data for this function.
|
||||
bool bailed_out_;
|
||||
@ -181,10 +177,44 @@ class ProducedPreParsedScopeData : public ZoneObject {
|
||||
|
||||
class ConsumedPreParsedScopeData {
|
||||
public:
|
||||
// Real data starts from index 1 (see data format description in the .cc
|
||||
// file).
|
||||
ConsumedPreParsedScopeData() : index_(1), child_index_(0) {}
|
||||
~ConsumedPreParsedScopeData() {}
|
||||
class ByteData {
|
||||
public:
|
||||
ByteData() : data_(nullptr), index_(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();
|
||||
|
||||
size_t RemainingBytes() const {
|
||||
DCHECK_NOT_NULL(data_);
|
||||
return data_->length() - index_;
|
||||
}
|
||||
|
||||
// private:
|
||||
PodArray<uint8_t>* data_;
|
||||
int index_;
|
||||
};
|
||||
|
||||
ConsumedPreParsedScopeData();
|
||||
~ConsumedPreParsedScopeData();
|
||||
|
||||
void SetData(Handle<PreParsedScopeData> data);
|
||||
|
||||
@ -205,14 +235,14 @@ class ConsumedPreParsedScopeData {
|
||||
void SkipFunctionDataForTesting();
|
||||
|
||||
private:
|
||||
void RestoreData(Scope* scope, PodArray<uint32_t>* scope_data);
|
||||
void RestoreDataForVariable(Variable* var, PodArray<uint32_t>* scope_data);
|
||||
void RestoreDataForInnerScopes(Scope* scope, PodArray<uint32_t>* scope_data);
|
||||
void RestoreData(Scope* scope);
|
||||
void RestoreDataForVariable(Variable* var);
|
||||
void RestoreDataForInnerScopes(Scope* scope);
|
||||
|
||||
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 index_;
|
||||
int child_index_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ConsumedPreParsedScopeData);
|
||||
|
@ -799,3 +799,35 @@ TEST(Regress753896) {
|
||||
// error is not detected inside lazy functions, but it might be in the future.
|
||||
i::parsing::ParseProgram(&info, isolate);
|
||||
}
|
||||
|
||||
TEST(ProducingAndConsumingByteData) {
|
||||
i::Isolate* isolate = CcTest::i_isolate();
|
||||
i::HandleScope scope(isolate);
|
||||
LocalContext env;
|
||||
|
||||
i::Zone zone(isolate->allocator(), ZONE_NAME);
|
||||
i::ProducedPreParsedScopeData::ByteData bytes(&zone);
|
||||
// Write some data.
|
||||
bytes.WriteUint32(1983); // This will be overwritten.
|
||||
bytes.WriteUint32(2147483647);
|
||||
bytes.WriteUint8(4);
|
||||
bytes.WriteUint8(255);
|
||||
bytes.WriteUint32(0);
|
||||
bytes.WriteUint8(0);
|
||||
bytes.OverwriteFirstUint32(2017);
|
||||
bytes.WriteUint8(100);
|
||||
|
||||
i::Handle<i::PodArray<uint8_t>> data_on_heap = bytes.Serialize(isolate);
|
||||
i::ConsumedPreParsedScopeData::ByteData bytes_for_reading;
|
||||
i::ConsumedPreParsedScopeData::ByteData::ReadingScope reading_scope(
|
||||
&bytes_for_reading, *data_on_heap);
|
||||
|
||||
// Read the data back.
|
||||
CHECK_EQ(bytes_for_reading.ReadUint32(), 2017);
|
||||
CHECK_EQ(bytes_for_reading.ReadUint32(), 2147483647);
|
||||
CHECK_EQ(bytes_for_reading.ReadUint8(), 4);
|
||||
CHECK_EQ(bytes_for_reading.ReadUint8(), 255);
|
||||
CHECK_EQ(bytes_for_reading.ReadUint32(), 0);
|
||||
CHECK_EQ(bytes_for_reading.ReadUint8(), 0);
|
||||
CHECK_EQ(bytes_for_reading.ReadUint8(), 100);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user