[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:
parent
1a95d4de81
commit
61b217b1cb
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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>)
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user