// Copyright 2017 the V8 project authors. All rights reserved.
|
// Use of this source code is governed by a BSD-style license that can be
|
// found in the LICENSE file.
|
|
#include "src/parsing/preparsed-scope-data.h"
|
|
#include "src/ast/scopes.h"
|
#include "src/ast/variables.h"
|
#include "src/handles.h"
|
#include "src/objects-inl.h"
|
#include "src/objects/shared-function-info.h"
|
#include "src/parsing/preparser.h"
|
|
namespace v8 {
|
namespace internal {
|
|
namespace {
|
|
class ScopeCallsSloppyEvalField : public BitField<bool, 0, 1> {};
|
class InnerScopeCallsEvalField
|
: public BitField<bool, ScopeCallsSloppyEvalField::kNext, 1> {};
|
|
class VariableMaybeAssignedField : public BitField8<bool, 0, 1> {};
|
class VariableContextAllocatedField
|
: public BitField8<bool, VariableMaybeAssignedField::kNext, 1> {};
|
|
|
#ifdef DEBUG
|
const int kMagicValue = 0xC0DE0DE;
|
|
const size_t kUint32Size = 5;
|
const size_t kUint8Size = 2;
|
const size_t kQuarterMarker = 0;
|
const size_t kPlaceholderSize = kUint32Size;
|
#else
|
const size_t kUint32Size = 4;
|
const size_t kUint8Size = 1;
|
const size_t kPlaceholderSize = 0;
|
#endif
|
|
const size_t kSkippableFunctionDataSize = 4 * kUint32Size + 1 * kUint8Size;
|
|
class LanguageField : public BitField8<LanguageMode, 0, 1> {};
|
class UsesSuperField : public BitField8<bool, LanguageField::kNext, 1> {};
|
STATIC_ASSERT(LanguageModeSize <= LanguageField::kNumValues);
|
|
} // namespace
|
|
/*
|
|
Internal data format for the backing store of ProducedPreparsedScopeData and
|
PreParsedScopeData::scope_data (on the heap):
|
|
(Skippable function data:)
|
------------------------------------
|
| scope_data_start (debug only) |
|
------------------------------------
|
| data for inner function 1 |
|
| ... |
|
------------------------------------
|
| data for inner function n |
|
| ... |
|
------------------------------------
|
(Scope allocation data:) << scope_data_start points here in debug
|
------------------------------------
|
magic value (debug only)
|
------------------------------------
|
scope positions (debug only)
|
------------------------------------
|
| scope type << only in debug |
|
| eval |
|
| ---------------------- |
|
| | data for variables | |
|
| | ... | |
|
| ---------------------- |
|
------------------------------------
|
------------------------------------
|
| data for inner scope 1 | << but not for function scopes
|
| ... |
|
------------------------------------
|
...
|
------------------------------------
|
| data for inner scope m |
|
| ... |
|
------------------------------------
|
|
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++);
|
}
|
free_quarters_in_last_byte_ = 0;
|
}
|
|
#ifdef DEBUG
|
void ProducedPreParsedScopeData::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;
|
const uint8_t* d = reinterpret_cast<uint8_t*>(&data);
|
for (size_t i = 0; i < 4; ++i) {
|
*it++ = *d++;
|
}
|
}
|
#endif
|
|
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);
|
free_quarters_in_last_byte_ = 0;
|
}
|
|
void ProducedPreParsedScopeData::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);
|
}
|
|
Handle<PodArray<uint8_t>> ProducedPreParsedScopeData::ByteData::Serialize(
|
Isolate* isolate) {
|
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);
|
}
|
#ifdef DEBUG
|
// Reserve space for scope_data_start, written later:
|
byte_data_->WriteUint32(0);
|
#endif
|
}
|
|
// 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),
|
preparser_(preparser),
|
produced_preparsed_scope_data_(nullptr) {
|
if (FLAG_preparser_scope_analysis) {
|
ProducedPreParsedScopeData* parent =
|
preparser->produced_preparsed_scope_data();
|
Zone* main_zone = preparser->main_zone();
|
produced_preparsed_scope_data_ =
|
new (main_zone) ProducedPreParsedScopeData(main_zone, parent);
|
preparser->set_produced_preparsed_scope_data(
|
produced_preparsed_scope_data_);
|
function_scope->set_produced_preparsed_scope_data(
|
produced_preparsed_scope_data_);
|
}
|
}
|
|
ProducedPreParsedScopeData::DataGatheringScope::~DataGatheringScope() {
|
if (FLAG_preparser_scope_analysis) {
|
preparser_->set_produced_preparsed_scope_data(
|
produced_preparsed_scope_data_->parent_);
|
}
|
}
|
|
void ProducedPreParsedScopeData::DataGatheringScope::MarkFunctionAsSkippable(
|
int end_position, int num_inner_functions) {
|
DCHECK(FLAG_preparser_scope_analysis);
|
DCHECK_NOT_NULL(produced_preparsed_scope_data_);
|
DCHECK_NOT_NULL(produced_preparsed_scope_data_->parent_);
|
produced_preparsed_scope_data_->parent_->AddSkippableFunction(
|
function_scope_->start_position(), end_position,
|
function_scope_->num_parameters(), num_inner_functions,
|
function_scope_->language_mode(), function_scope_->NeedsHomeObject());
|
}
|
|
void ProducedPreParsedScopeData::AddSkippableFunction(
|
int start_position, int end_position, int num_parameters,
|
int num_inner_functions, LanguageMode language_mode,
|
bool uses_super_property) {
|
DCHECK(FLAG_preparser_scope_analysis);
|
DCHECK(previously_produced_preparsed_scope_data_.is_null());
|
|
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);
|
byte_data_->WriteUint32(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 ProducedPreParsedScopeData::SaveScopeAllocationData(
|
DeclarationScope* scope) {
|
DCHECK(FLAG_preparser_scope_analysis);
|
DCHECK(previously_produced_preparsed_scope_data_.is_null());
|
// The data contains a uint32 (reserved space for scope_data_start) and
|
// function data items, kSkippableFunctionDataSize each.
|
DCHECK_GE(byte_data_->size(), kPlaceholderSize);
|
DCHECK_LE(byte_data_->size(), std::numeric_limits<uint32_t>::max());
|
DCHECK_EQ(byte_data_->size() % kSkippableFunctionDataSize, kPlaceholderSize);
|
|
if (bailed_out_) {
|
return;
|
}
|
|
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 (scope_data_start == kPlaceholderSize) {
|
return;
|
}
|
|
#ifdef DEBUG
|
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());
|
#endif
|
|
SaveDataForScope(scope);
|
}
|
|
bool ProducedPreParsedScopeData::ContainsInnerFunctions() const {
|
return byte_data_->size() > kPlaceholderSize;
|
}
|
|
MaybeHandle<PreParsedScopeData> ProducedPreParsedScopeData::Serialize(
|
Isolate* isolate) {
|
if (!previously_produced_preparsed_scope_data_.is_null()) {
|
DCHECK(!bailed_out_);
|
DCHECK_EQ(data_for_inner_functions_.size(), 0);
|
return previously_produced_preparsed_scope_data_;
|
}
|
if (bailed_out_) {
|
return MaybeHandle<PreParsedScopeData>();
|
}
|
|
DCHECK(!ThisOrParentBailedOut());
|
|
if (byte_data_->size() <= kPlaceholderSize) {
|
// The data contains only the placeholder.
|
return MaybeHandle<PreParsedScopeData>();
|
}
|
|
int child_data_length = static_cast<int>(data_for_inner_functions_.size());
|
Handle<PreParsedScopeData> data =
|
isolate->factory()->NewPreParsedScopeData(child_data_length);
|
|
Handle<PodArray<uint8_t>> scope_data_array = byte_data_->Serialize(isolate);
|
data->set_scope_data(*scope_data_array);
|
|
int i = 0;
|
for (const auto& item : data_for_inner_functions_) {
|
Handle<PreParsedScopeData> child_data;
|
if (item->Serialize(isolate).ToHandle(&child_data)) {
|
data->set_child_data(i, *child_data);
|
} else {
|
DCHECK(data->child_data(i)->IsNull());
|
}
|
i++;
|
}
|
|
return data;
|
}
|
|
bool ProducedPreParsedScopeData::ScopeNeedsData(Scope* scope) {
|
if (scope->scope_type() == ScopeType::FUNCTION_SCOPE) {
|
// Default constructors don't need data (they cannot contain inner functions
|
// defined by the user). Other functions do.
|
return !IsDefaultConstructor(scope->AsDeclarationScope()->function_kind());
|
}
|
if (!scope->is_hidden()) {
|
for (Variable* var : *scope->locals()) {
|
if (IsDeclaredVariableMode(var->mode())) {
|
return true;
|
}
|
}
|
}
|
for (Scope* inner = scope->inner_scope(); inner != nullptr;
|
inner = inner->sibling()) {
|
if (ScopeNeedsData(inner)) {
|
return true;
|
}
|
}
|
return false;
|
}
|
|
bool ProducedPreParsedScopeData::ScopeIsSkippableFunctionScope(Scope* scope) {
|
// Lazy non-arrow function scopes are skippable. Lazy functions are exactly
|
// those Scopes which have their own ProducedPreParsedScopeData 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->produced_preparsed_scope_data() != nullptr;
|
}
|
|
void ProducedPreParsedScopeData::SaveDataForScope(Scope* scope) {
|
DCHECK_NE(scope->end_position(), kNoSourcePosition);
|
|
if (!ScopeNeedsData(scope)) {
|
return;
|
}
|
|
#ifdef DEBUG
|
byte_data_->WriteUint8(scope->scope_type());
|
#endif
|
|
uint8_t eval =
|
ScopeCallsSloppyEvalField::encode(
|
scope->is_declaration_scope() &&
|
scope->AsDeclarationScope()->calls_sloppy_eval()) |
|
InnerScopeCallsEvalField::encode(scope->inner_scope_calls_eval());
|
byte_data_->WriteUint8(eval);
|
|
if (scope->scope_type() == ScopeType::FUNCTION_SCOPE) {
|
Variable* function = scope->AsDeclarationScope()->function_var();
|
if (function != nullptr) {
|
SaveDataForVariable(function);
|
}
|
}
|
|
for (Variable* var : *scope->locals()) {
|
if (IsDeclaredVariableMode(var->mode())) {
|
SaveDataForVariable(var);
|
}
|
}
|
|
SaveDataForInnerScopes(scope);
|
}
|
|
void ProducedPreParsedScopeData::SaveDataForVariable(Variable* var) {
|
#ifdef DEBUG
|
// 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());
|
for (int i = 0; i < name->length(); ++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);
|
}
|
|
void ProducedPreParsedScopeData::SaveDataForInnerScopes(Scope* scope) {
|
// Inner scopes are stored in the reverse order, but we'd like to write the
|
// data in the logical order. There might be many inner scopes, so we don't
|
// want to recurse here.
|
std::vector<Scope*> scopes;
|
for (Scope* inner = scope->inner_scope(); inner != nullptr;
|
inner = inner->sibling()) {
|
if (ScopeIsSkippableFunctionScope(inner)) {
|
// Don't save data about function scopes, since they'll have their own
|
// ProducedPreParsedScopeData where their data is saved.
|
DCHECK_NOT_NULL(
|
inner->AsDeclarationScope()->produced_preparsed_scope_data());
|
continue;
|
}
|
scopes.push_back(inner);
|
}
|
for (auto it = scopes.rbegin(); it != scopes.rend(); ++it) {
|
SaveDataForScope(*it);
|
}
|
}
|
|
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_++);
|
}
|
stored_quarters_ = 0;
|
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
|
stored_quarters_ = 0;
|
return data_->get(index_++);
|
}
|
|
uint8_t ConsumedPreParsedScopeData::ByteData::ReadQuarter() {
|
DCHECK_NOT_NULL(data_);
|
if (stored_quarters_ == 0) {
|
DCHECK_GE(RemainingBytes(), kUint8Size);
|
#ifdef DEBUG
|
// Check that there indeed are quarters following.
|
DCHECK_EQ(data_->get(index_++), kQuarterMarker);
|
#endif
|
stored_byte_ = data_->get(index_++);
|
stored_quarters_ = 4;
|
}
|
// Read the first 2 bits from stored_byte_.
|
uint8_t result = (stored_byte_ >> 6) & 3;
|
DCHECK_LE(result, 3);
|
--stored_quarters_;
|
stored_byte_ <<= 2;
|
return result;
|
}
|
|
size_t ConsumedPreParsedScopeData::ByteData::RemainingBytes() const {
|
DCHECK_NOT_NULL(data_);
|
return data_->length() - index_;
|
}
|
|
ConsumedPreParsedScopeData::ConsumedPreParsedScopeData()
|
: isolate_(nullptr), scope_data_(new ByteData()), child_index_(0) {}
|
|
ConsumedPreParsedScopeData::~ConsumedPreParsedScopeData() {}
|
|
void ConsumedPreParsedScopeData::SetData(Isolate* isolate,
|
Handle<PreParsedScopeData> data) {
|
DCHECK_NOT_NULL(isolate);
|
DCHECK(data->IsPreParsedScopeData());
|
isolate_ = isolate;
|
data_ = data;
|
#ifdef DEBUG
|
ByteData::ReadingScope reading_scope(this);
|
int scope_data_start = scope_data_->ReadUint32();
|
scope_data_->SetPosition(scope_data_start);
|
DCHECK_EQ(scope_data_->ReadUint32(), kMagicValue);
|
// The first data item is scope_data_start. Skip over it.
|
scope_data_->SetPosition(kPlaceholderSize);
|
#endif
|
}
|
|
ProducedPreParsedScopeData*
|
ConsumedPreParsedScopeData::GetDataForSkippableFunction(
|
Zone* zone, int start_position, int* end_position, int* num_parameters,
|
int* num_inner_functions, bool* uses_super_property,
|
LanguageMode* language_mode) {
|
// The skippable function *must* be the next function in the data. Use the
|
// start position as a sanity check.
|
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_->ReadUint32();
|
DCHECK_GT(*end_position, start_position);
|
*num_parameters = scope_data_->ReadUint32();
|
*num_inner_functions = scope_data_->ReadUint32();
|
|
uint8_t language_and_super = scope_data_->ReadQuarter();
|
*language_mode = LanguageMode(LanguageField::decode(language_and_super));
|
*uses_super_property = UsesSuperField::decode(language_and_super);
|
|
// 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.
|
CHECK_GT(data_->length(), child_index_);
|
Object* child_data = data_->child_data(child_index_++);
|
if (!child_data->IsPreParsedScopeData()) {
|
return nullptr;
|
}
|
Handle<PreParsedScopeData> child_data_handle(
|
PreParsedScopeData::cast(child_data), isolate_);
|
return new (zone) ProducedPreParsedScopeData(child_data_handle, zone);
|
}
|
|
void ConsumedPreParsedScopeData::RestoreScopeAllocationData(
|
DeclarationScope* scope) {
|
DCHECK(FLAG_preparser_scope_analysis);
|
DCHECK_EQ(scope->scope_type(), ScopeType::FUNCTION_SCOPE);
|
DCHECK(!data_.is_null());
|
|
ByteData::ReadingScope reading_scope(this);
|
|
#ifdef DEBUG
|
int magic_value_from_data = scope_data_->ReadUint32();
|
// Check that we've consumed all inner function data.
|
DCHECK_EQ(magic_value_from_data, kMagicValue);
|
|
int start_position_from_data = scope_data_->ReadUint32();
|
int end_position_from_data = scope_data_->ReadUint32();
|
DCHECK_EQ(start_position_from_data, scope->start_position());
|
DCHECK_EQ(end_position_from_data, scope->end_position());
|
#endif
|
|
RestoreData(scope);
|
|
// Check that we consumed all scope data.
|
DCHECK_EQ(scope_data_->RemainingBytes(), 0);
|
}
|
|
void ConsumedPreParsedScopeData::RestoreData(Scope* scope) {
|
if (scope->is_declaration_scope() &&
|
scope->AsDeclarationScope()->is_skipped_function()) {
|
return;
|
}
|
|
// It's possible that scope is not present in the data at all (since PreParser
|
// doesn't create the corresponding scope). In this case, the Scope won't
|
// contain any variables for which we need the data.
|
if (!ProducedPreParsedScopeData::ScopeNeedsData(scope)) {
|
return;
|
}
|
|
if (scope_data_->RemainingBytes() < kUint8Size) {
|
// Temporary debugging code for detecting inconsistent data. Write debug
|
// information on the stack, then crash.
|
isolate_->PushStackTraceAndDie();
|
}
|
|
// scope_type is stored only in debug mode.
|
CHECK_GE(scope_data_->RemainingBytes(), kUint8Size);
|
DCHECK_EQ(scope_data_->ReadUint8(), scope->scope_type());
|
|
uint32_t eval = scope_data_->ReadUint8();
|
if (ScopeCallsSloppyEvalField::decode(eval)) {
|
scope->RecordEvalCall();
|
}
|
if (InnerScopeCallsEvalField::decode(eval)) {
|
scope->RecordInnerScopeEvalCall();
|
}
|
|
if (scope->scope_type() == ScopeType::FUNCTION_SCOPE) {
|
Variable* function = scope->AsDeclarationScope()->function_var();
|
if (function != nullptr) {
|
RestoreDataForVariable(function);
|
}
|
}
|
|
for (Variable* var : *scope->locals()) {
|
if (IsDeclaredVariableMode(var->mode())) {
|
RestoreDataForVariable(var);
|
}
|
}
|
|
RestoreDataForInnerScopes(scope);
|
}
|
|
void ConsumedPreParsedScopeData::RestoreDataForVariable(Variable* var) {
|
#ifdef DEBUG
|
const AstRawString* name = var->raw_name();
|
bool data_one_byte = scope_data_->ReadUint8();
|
DCHECK_IMPLIES(name->is_one_byte(), data_one_byte);
|
DCHECK_EQ(scope_data_->ReadUint32(), static_cast<uint32_t>(name->length()));
|
if (!name->is_one_byte() && data_one_byte) {
|
// It's possible that "name" is a two-byte representation of the string
|
// stored in the data.
|
for (int i = 0; i < 2 * name->length(); i += 2) {
|
#if defined(V8_TARGET_LITTLE_ENDIAN)
|
DCHECK_EQ(scope_data_->ReadUint8(), name->raw_data()[i]);
|
DCHECK_EQ(0, name->raw_data()[i + 1]);
|
#else
|
DCHECK_EQ(scope_data_->ReadUint8(), name->raw_data()[i + 1]);
|
DCHECK_EQ(0, name->raw_data()[i]);
|
#endif // V8_TARGET_LITTLE_ENDIAN
|
}
|
} else {
|
for (int i = 0; i < name->length(); ++i) {
|
DCHECK_EQ(scope_data_->ReadUint8(), name->raw_data()[i]);
|
}
|
}
|
#endif
|
uint8_t variable_data = scope_data_->ReadQuarter();
|
if (VariableMaybeAssignedField::decode(variable_data)) {
|
var->set_maybe_assigned();
|
}
|
if (VariableContextAllocatedField::decode(variable_data)) {
|
var->set_is_used();
|
var->ForceContextAllocation();
|
}
|
}
|
|
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);
|
}
|
}
|
|
} // namespace internal
|
} // namespace v8
|