Commit 1191e6f6 authored by Marja Hölttä's avatar Marja Hölttä Committed by Commit Bot

[parser] Skipping inner funcs: store and use the inner function data.

The data needed to be modified a bit to actually allow skipping over functions
based on it. In particular, we need to allow skipping over an unknown inner
scope structure (in the previous stage, we just had tests comparing the data
against some baseline truth, so it wasn't needed).

also removing the current "skip functions based on preparse data" logic,
since preparser data is not used any more. At a later stage, I'll consider
plugging the preparser-scope-analysis-data into that pipeline (so I don't want
to remove the full code yet).

Integration to the various forms of compilation is still incomplete; this CL
integrates just enough to get the minimal example to pass:

(function foo() {
  function preparsed() {
    var var1 = 10;
    function skip_me() {
      print(var1);
    }
    return skip_me;
  }
  return preparsed;
})()()();

BUG=v8:5516

Change-Id: I0d24b4c3b338f7e6b6c3bf7cf2c1ceb29608e2f2
Reviewed-on: https://chromium-review.googlesource.com/446336
Commit-Queue: Marja Hölttä <marja@chromium.org>
Reviewed-by: 's avatarDaniel Vogelheim <vogelheim@chromium.org>
Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/master@{#43908}
parent 3700a01c
...@@ -262,6 +262,9 @@ Scope::Scope(Zone* zone, ScopeType scope_type, Handle<ScopeInfo> scope_info) ...@@ -262,6 +262,9 @@ Scope::Scope(Zone* zone, ScopeType scope_type, Handle<ScopeInfo> scope_info)
set_language_mode(scope_info->language_mode()); set_language_mode(scope_info->language_mode());
num_heap_slots_ = scope_info->ContextLength(); num_heap_slots_ = scope_info->ContextLength();
DCHECK_LE(Context::MIN_CONTEXT_SLOTS, num_heap_slots_); DCHECK_LE(Context::MIN_CONTEXT_SLOTS, num_heap_slots_);
// We don't really need to use the preparsed scope data; this is just to
// shorten the recursion in SetMustUsePreParsedScopeData.
must_use_preparsed_scope_data_ = true;
} }
DeclarationScope::DeclarationScope(Zone* zone, ScopeType scope_type, DeclarationScope::DeclarationScope(Zone* zone, ScopeType scope_type,
...@@ -309,6 +312,7 @@ void DeclarationScope::SetDefaults() { ...@@ -309,6 +312,7 @@ void DeclarationScope::SetDefaults() {
rare_data_ = nullptr; rare_data_ = nullptr;
should_eager_compile_ = false; should_eager_compile_ = false;
was_lazily_parsed_ = false; was_lazily_parsed_ = false;
is_skipped_function_ = false;
#ifdef DEBUG #ifdef DEBUG
DeclarationScope* outer_declaration_scope = DeclarationScope* outer_declaration_scope =
outer_scope_ ? outer_scope_->GetDeclarationScope() : nullptr; outer_scope_ ? outer_scope_->GetDeclarationScope() : nullptr;
...@@ -345,6 +349,8 @@ void Scope::SetDefaults() { ...@@ -345,6 +349,8 @@ void Scope::SetDefaults() {
force_context_allocation_ = false; force_context_allocation_ = false;
is_declaration_scope_ = false; is_declaration_scope_ = false;
must_use_preparsed_scope_data_ = false;
} }
bool Scope::HasSimpleParameters() { bool Scope::HasSimpleParameters() {
...@@ -618,6 +624,7 @@ void DeclarationScope::Analyze(ParseInfo* info, AnalyzeMode mode) { ...@@ -618,6 +624,7 @@ void DeclarationScope::Analyze(ParseInfo* info, AnalyzeMode mode) {
&RuntimeCallStats::CompileScopeAnalysis); &RuntimeCallStats::CompileScopeAnalysis);
DCHECK(info->literal() != NULL); DCHECK(info->literal() != NULL);
DeclarationScope* scope = info->literal()->scope(); DeclarationScope* scope = info->literal()->scope();
DCHECK(scope->scope_info_.is_null());
Handle<ScopeInfo> outer_scope_info; Handle<ScopeInfo> outer_scope_info;
if (info->maybe_outer_scope_info().ToHandle(&outer_scope_info)) { if (info->maybe_outer_scope_info().ToHandle(&outer_scope_info)) {
...@@ -653,6 +660,13 @@ void DeclarationScope::Analyze(ParseInfo* info, AnalyzeMode mode) { ...@@ -653,6 +660,13 @@ void DeclarationScope::Analyze(ParseInfo* info, AnalyzeMode mode) {
// The outer scope is never lazy. // The outer scope is never lazy.
scope->set_should_eager_compile(); scope->set_should_eager_compile();
if (scope->must_use_preparsed_scope_data_) {
DCHECK(FLAG_preparser_scope_analysis);
DCHECK_NOT_NULL(info->preparsed_scope_data());
DCHECK_EQ(scope->scope_type_, ScopeType::FUNCTION_SCOPE);
info->preparsed_scope_data()->RestoreData(scope);
}
scope->AllocateVariables(info, mode); scope->AllocateVariables(info, mode);
// Ensuring that the outer script scope has a scope info avoids having // Ensuring that the outer script scope has a scope info avoids having
...@@ -1522,7 +1536,7 @@ void DeclarationScope::AnalyzePartially( ...@@ -1522,7 +1536,7 @@ void DeclarationScope::AnalyzePartially(
arguments_ = nullptr; arguments_ = nullptr;
} }
if (FLAG_preparser_scope_analysis) { if (FLAG_preparser_scope_analysis && preparsed_scope_data->Producing()) {
// Store the information needed for allocating the locals of this scope // Store the information needed for allocating the locals of this scope
// and its inner scopes. // and its inner scopes.
preparsed_scope_data->SaveData(this); preparsed_scope_data->SaveData(this);
......
...@@ -155,6 +155,20 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) { ...@@ -155,6 +155,20 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
Zone* zone() const { return zone_; } Zone* zone() const { return zone_; }
void SetMustUsePreParsedScopeData() {
if (must_use_preparsed_scope_data_) {
return;
}
must_use_preparsed_scope_data_ = true;
if (outer_scope_) {
outer_scope_->SetMustUsePreParsedScopeData();
}
}
bool must_use_preparsed_scope_data() const {
return must_use_preparsed_scope_data_;
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Declarations // Declarations
...@@ -556,6 +570,8 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) { ...@@ -556,6 +570,8 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
// True if it holds 'var' declarations. // True if it holds 'var' declarations.
bool is_declaration_scope_ : 1; bool is_declaration_scope_ : 1;
bool must_use_preparsed_scope_data_ : 1;
// Create a non-local variable with a given name. // Create a non-local variable with a given name.
// These variables are looked up dynamically at runtime. // These variables are looked up dynamically at runtime.
Variable* NonLocal(const AstRawString* name, VariableMode mode); Variable* NonLocal(const AstRawString* name, VariableMode mode);
...@@ -850,6 +866,11 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope { ...@@ -850,6 +866,11 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
void ResetAfterPreparsing(AstValueFactory* ast_value_factory, bool aborted); void ResetAfterPreparsing(AstValueFactory* ast_value_factory, bool aborted);
bool is_skipped_function() const { return is_skipped_function_; }
void set_is_skipped_function(bool is_skipped_function) {
is_skipped_function_ = is_skipped_function;
}
private: private:
void AllocateParameter(Variable* var, int index); void AllocateParameter(Variable* var, int index);
...@@ -886,6 +907,7 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope { ...@@ -886,6 +907,7 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
#if DEBUG #if DEBUG
bool is_being_lazily_parsed_ : 1; bool is_being_lazily_parsed_ : 1;
#endif #endif
bool is_skipped_function_ : 1;
// Parameter list in source order. // Parameter list in source order.
ZoneList<Variable*> params_; ZoneList<Variable*> params_;
......
...@@ -1152,6 +1152,14 @@ MaybeHandle<Code> GetLazyCode(Handle<JSFunction> function) { ...@@ -1152,6 +1152,14 @@ MaybeHandle<Code> GetLazyCode(Handle<JSFunction> function) {
ParseInfo parse_info(handle(function->shared())); ParseInfo parse_info(handle(function->shared()));
Zone compile_zone(isolate->allocator(), ZONE_NAME); Zone compile_zone(isolate->allocator(), ZONE_NAME);
CompilationInfo info(&compile_zone, &parse_info, function); CompilationInfo info(&compile_zone, &parse_info, function);
if (FLAG_preparser_scope_analysis) {
Handle<SharedFunctionInfo> shared(function->shared());
Handle<Script> script(Script::cast(function->shared()->script()));
if (script->HasPreparsedScopeData()) {
parse_info.preparsed_scope_data()->Deserialize(
script->GetPreparsedScopeData());
}
}
Handle<Code> result; Handle<Code> result;
ASSIGN_RETURN_ON_EXCEPTION( ASSIGN_RETURN_ON_EXCEPTION(
isolate, result, GetUnoptimizedCode(&info, Compiler::CONCURRENT), Code); isolate, result, GetUnoptimizedCode(&info, Compiler::CONCURRENT), Code);
...@@ -1234,8 +1242,14 @@ Handle<SharedFunctionInfo> CompileToplevel(CompilationInfo* info) { ...@@ -1234,8 +1242,14 @@ Handle<SharedFunctionInfo> CompileToplevel(CompilationInfo* info) {
PROFILE(isolate, CodeCreateEvent(log_tag, result->abstract_code(), *result, PROFILE(isolate, CodeCreateEvent(log_tag, result->abstract_code(), *result,
*script_name)); *script_name));
if (!script.is_null()) if (!script.is_null()) {
script->set_compilation_state(Script::COMPILATION_STATE_COMPILED); script->set_compilation_state(Script::COMPILATION_STATE_COMPILED);
if (FLAG_preparser_scope_analysis) {
Handle<FixedUint32Array> data(
parse_info->preparsed_scope_data()->Serialize(isolate));
script->set_preparsed_scope_data(*data);
}
}
} }
return result; return result;
......
...@@ -1111,6 +1111,7 @@ Handle<Script> Factory::NewScript(Handle<String> source) { ...@@ -1111,6 +1111,7 @@ Handle<Script> Factory::NewScript(Handle<String> source) {
script->set_eval_from_position(0); script->set_eval_from_position(0);
script->set_shared_function_infos(*empty_fixed_array(), SKIP_WRITE_BARRIER); script->set_shared_function_infos(*empty_fixed_array(), SKIP_WRITE_BARRIER);
script->set_flags(0); script->set_flags(0);
script->set_preparsed_scope_data(heap->empty_fixed_uint32_array());
heap->set_script_list(*WeakFixedArray::Add(script_list(), script)); heap->set_script_list(*WeakFixedArray::Add(script_list(), script));
return script; return script;
......
...@@ -5767,6 +5767,8 @@ ACCESSORS(Script, source_url, Object, kSourceUrlOffset) ...@@ -5767,6 +5767,8 @@ ACCESSORS(Script, source_url, Object, kSourceUrlOffset)
ACCESSORS(Script, source_mapping_url, Object, kSourceMappingUrlOffset) ACCESSORS(Script, source_mapping_url, Object, kSourceMappingUrlOffset)
ACCESSORS_CHECKED(Script, wasm_compiled_module, Object, kEvalFromSharedOffset, ACCESSORS_CHECKED(Script, wasm_compiled_module, Object, kEvalFromSharedOffset,
this->type() == TYPE_WASM) this->type() == TYPE_WASM)
ACCESSORS(Script, preparsed_scope_data, FixedTypedArrayBase,
kPreParsedScopeDataOffset)
Script::CompilationType Script::compilation_type() { Script::CompilationType Script::compilation_type() {
return BooleanBit::get(flags(), kCompilationTypeBit) ? return BooleanBit::get(flags(), kCompilationTypeBit) ?
......
...@@ -13260,6 +13260,15 @@ Script::Iterator::Iterator(Isolate* isolate) ...@@ -13260,6 +13260,15 @@ Script::Iterator::Iterator(Isolate* isolate)
Script* Script::Iterator::Next() { return iterator_.Next<Script>(); } Script* Script::Iterator::Next() { return iterator_.Next<Script>(); }
bool Script::HasPreparsedScopeData() const {
return preparsed_scope_data()->length() > 0;
}
Handle<FixedUint32Array> Script::GetPreparsedScopeData() const {
return Handle<FixedUint32Array>::cast(
Handle<FixedTypedArrayBase>(preparsed_scope_data()));
}
SharedFunctionInfo::ScriptIterator::ScriptIterator(Handle<Script> script) SharedFunctionInfo::ScriptIterator::ScriptIterator(Handle<Script> script)
: ScriptIterator(script->GetIsolate(), : ScriptIterator(script->GetIsolate(),
handle(script->shared_function_infos())) {} handle(script->shared_function_infos())) {}
......
...@@ -6641,6 +6641,8 @@ class Script: public Struct { ...@@ -6641,6 +6641,8 @@ class Script: public Struct {
// This must only be called if the type of this script is TYPE_WASM. // This must only be called if the type of this script is TYPE_WASM.
DECL_ACCESSORS(wasm_compiled_module, Object) DECL_ACCESSORS(wasm_compiled_module, Object)
DECL_ACCESSORS(preparsed_scope_data, FixedTypedArrayBase)
// [compilation_type]: how the the script was compiled. Encoded in the // [compilation_type]: how the the script was compiled. Encoded in the
// 'flags' field. // 'flags' field.
inline CompilationType compilation_type(); inline CompilationType compilation_type();
...@@ -6727,6 +6729,9 @@ class Script: public Struct { ...@@ -6727,6 +6729,9 @@ class Script: public Struct {
DISALLOW_COPY_AND_ASSIGN(Iterator); DISALLOW_COPY_AND_ASSIGN(Iterator);
}; };
bool HasPreparsedScopeData() const;
Handle<FixedUint32Array> GetPreparsedScopeData() const;
// Dispatched behavior. // Dispatched behavior.
DECLARE_PRINTER(Script) DECLARE_PRINTER(Script)
DECLARE_VERIFIER(Script) DECLARE_VERIFIER(Script)
...@@ -6748,7 +6753,9 @@ class Script: public Struct { ...@@ -6748,7 +6753,9 @@ class Script: public Struct {
static const int kFlagsOffset = kSharedFunctionInfosOffset + kPointerSize; static const int kFlagsOffset = kSharedFunctionInfosOffset + kPointerSize;
static const int kSourceUrlOffset = kFlagsOffset + kPointerSize; static const int kSourceUrlOffset = kFlagsOffset + kPointerSize;
static const int kSourceMappingUrlOffset = kSourceUrlOffset + kPointerSize; static const int kSourceMappingUrlOffset = kSourceUrlOffset + kPointerSize;
static const int kSize = kSourceMappingUrlOffset + kPointerSize; static const int kPreParsedScopeDataOffset =
kSourceMappingUrlOffset + kPointerSize;
static const int kSize = kPreParsedScopeDataOffset + kPointerSize;
private: private:
// Bit positions in the flags field. // Bit positions in the flags field.
......
...@@ -2826,6 +2826,21 @@ Parser::LazyParsingResult Parser::SkipFunction( ...@@ -2826,6 +2826,21 @@ Parser::LazyParsingResult Parser::SkipFunction(
function_scope->RecordEvalCall(); function_scope->RecordEvalCall();
} }
SkipFunctionLiterals(data.num_inner_functions); SkipFunctionLiterals(data.num_inner_functions);
}
}
// FIXME(marja): There are 3 ways to skip functions now. Unify them.
if (preparsed_scope_data_->Consuming()) {
DCHECK(FLAG_preparser_scope_analysis);
int end_pos = kNoSourcePosition;
if (preparsed_scope_data_->FindFunctionEnd(function_scope->start_position(),
&end_pos)) {
function_scope->set_end_position(end_pos);
function_scope->set_is_skipped_function(true);
function_scope->outer_scope()->SetMustUsePreParsedScopeData();
scanner()->SeekForward(end_pos - 1);
Expect(Token::RBRACE, CHECK_OK_VALUE(kLazyParsingComplete));
// FIXME(marja): SkipFunctionLiterals still needed.
return kLazyParsingComplete; return kLazyParsingComplete;
} }
} }
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "src/ast/scopes.h" #include "src/ast/scopes.h"
#include "src/ast/variables.h" #include "src/ast/variables.h"
#include "src/handles.h"
#include "src/objects-inl.h" #include "src/objects-inl.h"
namespace v8 { namespace v8 {
...@@ -19,35 +20,119 @@ class VariableMaybeAssignedField ...@@ -19,35 +20,119 @@ class VariableMaybeAssignedField
class VariableContextAllocatedField class VariableContextAllocatedField
: public BitField16<bool, VariableMaybeAssignedField::kNext, 1> {}; : public BitField16<bool, VariableMaybeAssignedField::kNext, 1> {};
const int kFunctionDataSize = 3;
} // namespace } // namespace
/*
Internal data format for the backing store:
------------------------------------
| scope type << only in debug |
| inner_scope_calls_eval_ |
| data end index |
| ---------------------- |
| | data for variables | |
| | ... | |
| ---------------------- |
------------------------------------
------------------------------------
| data for inner scope_1 |
| ... |
------------------------------------
...
------------------------------------
| data for inner scope_n |
| ... |
------------------------------------
<< data end index points here
*/
void PreParsedScopeData::SaveData(Scope* scope) { void PreParsedScopeData::SaveData(Scope* scope) {
size_t old_size = backing_store_.size(); DCHECK(!has_data_);
if (scope->scope_type() == ScopeType::FUNCTION_SCOPE) {
function_index_[scope->start_position()] =
std::make_pair(scope->end_position(), backing_store_.size());
}
if (!ScopeNeedsData(scope)) {
return;
}
#ifdef DEBUG
backing_store_.push_back(scope->scope_type());
#endif
backing_store_.push_back(scope->inner_scope_calls_eval());
// Reserve space for the data end index (which we don't know yet). The end
// index is needed for skipping over data for a function scope when we skip
// parsing of the corresponding function.
size_t data_end_index = backing_store_.size();
backing_store_.push_back(-1);
if (!scope->is_hidden()) { if (!scope->is_hidden()) {
for (Variable* var : *scope->locals()) { for (Variable* var : *scope->locals()) {
if (var->mode() == VAR || var->mode() == LET || var->mode() == CONST) { if (IsDeclaredVariableMode(var->mode())) {
SaveDataForVariable(var); SaveDataForVariable(var);
} }
} }
} }
for (Scope* inner = scope->inner_scope(); inner != nullptr;
inner = inner->sibling()) { SaveDataForInnerScopes(scope);
SaveData(inner);
backing_store_[data_end_index] = backing_store_.size();
}
void PreParsedScopeData::RestoreData(DeclarationScope* scope) const {
int index = -1;
DCHECK_EQ(scope->scope_type(), ScopeType::FUNCTION_SCOPE);
bool success = FindFunctionData(scope->start_position(), &index);
DCHECK(success);
USE(success);
RestoreData(scope, &index);
}
void PreParsedScopeData::RestoreData(Scope* scope, int* index_ptr) const {
// 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 (!ScopeNeedsData(scope) && !IsSkippedFunctionScope(scope)) {
return;
} }
if (old_size != backing_store_.size()) { int& index = *index_ptr;
#ifdef DEBUG #ifdef DEBUG
backing_store_.push_back(scope->scope_type()); // Data integrity check.
if (scope->scope_type() == ScopeType::FUNCTION_SCOPE) {
int end_position_from_data = -1;
FindFunctionEnd(scope->start_position(), &end_position_from_data);
DCHECK_EQ(end_position_from_data, scope->end_position());
int index_from_data = -1;
FindFunctionData(scope->start_position(), &index_from_data);
DCHECK_EQ(index_from_data, index);
}
#endif #endif
backing_store_.push_back(scope->inner_scope_calls_eval()); if (IsSkippedFunctionScope(scope)) {
// This scope is a function scope representing a function we want to
// skip. So just skip over its data.
DCHECK(!scope->must_use_preparsed_scope_data());
index = backing_store_[index + 2];
return;
} }
}
void PreParsedScopeData::RestoreData(Scope* scope, int* index_ptr) const { DCHECK_EQ(backing_store_[index++], scope->scope_type());
int& index = *index_ptr;
int old_index = index; if (backing_store_[index++]) {
scope->RecordEvalCall();
}
int data_end_index = backing_store_[index++];
USE(data_end_index);
if (!scope->is_hidden()) { if (!scope->is_hidden()) {
for (Variable* var : *scope->locals()) { for (Variable* var : *scope->locals()) {
...@@ -56,22 +141,65 @@ void PreParsedScopeData::RestoreData(Scope* scope, int* index_ptr) const { ...@@ -56,22 +141,65 @@ void PreParsedScopeData::RestoreData(Scope* scope, int* index_ptr) const {
} }
} }
} }
for (Scope* inner = scope->inner_scope(); inner != nullptr;
inner = inner->sibling()) { RestoreDataForInnerScopes(scope, index_ptr);
RestoreData(inner, index_ptr);
DCHECK_EQ(data_end_index, index);
}
FixedUint32Array* PreParsedScopeData::Serialize(Isolate* isolate) const {
// FIXME(marja): save space by using a byte array and converting
// function_index_ to bytes.
Handle<JSTypedArray> js_array = isolate->factory()->NewJSTypedArray(
UINT32_ELEMENTS,
function_index_.size() * kFunctionDataSize + backing_store_.size() + 1);
FixedUint32Array* array = FixedUint32Array::cast(js_array->elements());
array->set(0, static_cast<uint32_t>(function_index_.size()));
int i = 1;
for (const auto& item : function_index_) {
array->set(i++, item.first);
array->set(i++, item.second.first);
array->set(i++, item.second.second);
} }
if (index != old_index) { for (size_t j = 0; j < backing_store_.size(); ++j) {
// Some data was read, i.e., there's data for the Scope. array->set(i++, static_cast<uint32_t>(backing_store_[j]));
}
return array;
}
#ifdef DEBUG void PreParsedScopeData::Deserialize(Handle<FixedUint32Array> array) {
DCHECK_EQ(backing_store_[index++], scope->scope_type()); has_data_ = true;
#endif DCHECK(!array.is_null());
if (array->length() == 0) {
return;
}
int function_count = array->get_scalar(0);
CHECK(array->length() > function_count * kFunctionDataSize);
if (function_count == 0) {
return;
}
int i = 1;
for (; i < function_count * kFunctionDataSize + 1; i += kFunctionDataSize) {
function_index_[array->get_scalar(i)] =
std::make_pair(array->get_scalar(i + 1), array->get_scalar(i + 2));
}
CHECK_EQ(function_index_.size(), function_count);
if (backing_store_[index++]) { backing_store_.reserve(array->length() - i);
scope->RecordEvalCall(); for (; i < array->length(); ++i) {
backing_store_.push_back(array->get_scalar(i));
} }
}
bool PreParsedScopeData::FindFunctionEnd(int start_pos, int* end_pos) const {
auto it = function_index_.find(start_pos);
if (it == function_index_.end()) {
return false;
} }
*end_pos = it->second.first;
return true;
} }
void PreParsedScopeData::SaveDataForVariable(Variable* var) { void PreParsedScopeData::SaveDataForVariable(Variable* var) {
...@@ -84,7 +212,8 @@ void PreParsedScopeData::SaveDataForVariable(Variable* var) { ...@@ -84,7 +212,8 @@ void PreParsedScopeData::SaveDataForVariable(Variable* var) {
backing_store_.push_back(name->raw_data()[i]); backing_store_.push_back(name->raw_data()[i]);
} }
#endif #endif
int variable_data = VariableIsUsedField::encode(var->is_used()) | // FIXME(marja): Only 3 bits needed, not a full byte.
byte variable_data = VariableIsUsedField::encode(var->is_used()) |
VariableMaybeAssignedField::encode( VariableMaybeAssignedField::encode(
var->maybe_assigned() == kMaybeAssigned) | var->maybe_assigned() == kMaybeAssigned) |
VariableContextAllocatedField::encode( VariableContextAllocatedField::encode(
...@@ -103,7 +232,7 @@ void PreParsedScopeData::RestoreDataForVariable(Variable* var, ...@@ -103,7 +232,7 @@ void PreParsedScopeData::RestoreDataForVariable(Variable* var,
DCHECK_EQ(backing_store_[index++], name->raw_data()[i]); DCHECK_EQ(backing_store_[index++], name->raw_data()[i]);
} }
#endif #endif
int variable_data = backing_store_[index++]; byte variable_data = backing_store_[index++];
if (VariableIsUsedField::decode(variable_data)) { if (VariableIsUsedField::decode(variable_data)) {
var->set_is_used(); var->set_is_used();
} }
...@@ -115,5 +244,65 @@ void PreParsedScopeData::RestoreDataForVariable(Variable* var, ...@@ -115,5 +244,65 @@ void PreParsedScopeData::RestoreDataForVariable(Variable* var,
} }
} }
void PreParsedScopeData::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()) {
scopes.push_back(inner);
}
for (int i = static_cast<int>(scopes.size()) - 1; i >= 0; --i) {
SaveData(scopes[i]);
}
}
void PreParsedScopeData::RestoreDataForInnerScopes(Scope* scope,
int* index_ptr) const {
std::vector<Scope*> scopes;
for (Scope* inner = scope->inner_scope(); inner != nullptr;
inner = inner->sibling()) {
scopes.push_back(inner);
}
for (int i = static_cast<int>(scopes.size()) - 1; i >= 0; --i) {
RestoreData(scopes[i], index_ptr);
}
}
bool PreParsedScopeData::FindFunctionData(int start_pos, int* index) const {
auto it = function_index_.find(start_pos);
if (it == function_index_.end()) {
return false;
}
*index = it->second.second;
return true;
}
bool PreParsedScopeData::ScopeNeedsData(Scope* scope) {
if (scope->scope_type() == ScopeType::FUNCTION_SCOPE) {
return true;
}
if (!scope->is_hidden()) {
for (Variable* var : *scope->locals()) {
if (var->mode() == VAR || var->mode() == LET || var->mode() == CONST) {
return true;
}
}
}
for (Scope* inner = scope->inner_scope(); inner != nullptr;
inner = inner->sibling()) {
if (ScopeNeedsData(inner)) {
return true;
}
}
return false;
}
bool PreParsedScopeData::IsSkippedFunctionScope(Scope* scope) {
return scope->is_declaration_scope() &&
scope->AsDeclarationScope()->is_skipped_function();
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -5,13 +5,56 @@ ...@@ -5,13 +5,56 @@
#ifndef V8_PARSING_PREPARSED_SCOPE_DATA_H_ #ifndef V8_PARSING_PREPARSED_SCOPE_DATA_H_
#define V8_PARSING_PREPARSED_SCOPE_DATA_H_ #define V8_PARSING_PREPARSED_SCOPE_DATA_H_
#include <unordered_map>
#include <vector> #include <vector>
#include "src/globals.h" #include "src/globals.h"
#include "src/objects.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
template <typename T>
class Handle;
/*
Skipping inner functions.
Consider the following code:
(function eager_outer() {
function lazy_inner() {
let a;
function skip_me() { a; }
}
return lazy_inner;
})();
... lazy_inner(); ...
When parsing the code the first time, eager_outer is parsed and lazy_inner
(and everything inside it) is preparsed. When lazy_inner is called, we don't
want to parse or preparse skip_me again. Instead, we want to skip over it,
since it has already been preparsed once.
In order to be able to do this, we need to store the information needed for
allocating the variables in lazy_inner when we preparse it, and then later do
scope allocation based on that data.
We need the following data for each scope in lazy_inner's scope tree:
For each Variable:
- is_used
- maybe_assigned
- has_forced_context_allocation
For each Scope:
- inner_scope_calls_eval_.
PreParsedScopeData implements storing and restoring the above mentioned data.
*/
class PreParsedScopeData { class PreParsedScopeData {
public: public:
PreParsedScopeData() {} PreParsedScopeData() {}
...@@ -24,16 +67,36 @@ class PreParsedScopeData { ...@@ -24,16 +67,36 @@ class PreParsedScopeData {
// Restores the information needed for allocating the Scopes's (and its // Restores the information needed for allocating the Scopes's (and its
// subscopes') variables. // subscopes') variables.
void RestoreData(Scope* scope, int* index_ptr) const; void RestoreData(Scope* scope, int* index_ptr) const;
void RestoreData(DeclarationScope* scope) const;
FixedUint32Array* Serialize(Isolate* isolate) const;
void Deserialize(Handle<FixedUint32Array> array);
bool Consuming() const { return has_data_; }
bool Producing() const { return !has_data_; }
bool FindFunctionEnd(int start_pos, int* end_pos) const;
private: private:
friend class ScopeTestHelper; friend class ScopeTestHelper;
void SaveDataForVariable(Variable* var); void SaveDataForVariable(Variable* var);
void RestoreDataForVariable(Variable* var, int* index_ptr) const; void RestoreDataForVariable(Variable* var, int* index_ptr) const;
void SaveDataForInnerScopes(Scope* scope);
void RestoreDataForInnerScopes(Scope* scope, int* index_ptr) const;
bool FindFunctionData(int start_pos, int* index) const;
static bool ScopeNeedsData(Scope* scope);
static bool IsSkippedFunctionScope(Scope* scope);
// TODO(marja): Make the backing store more efficient once we know exactly // TODO(marja): Make the backing store more efficient once we know exactly
// what data is needed. // what data is needed.
std::vector<byte> backing_store_; std::vector<byte> backing_store_;
// Start pos -> (end pos, index in data)
std::unordered_map<uint32_t, std::pair<uint32_t, uint32_t>> function_index_;
bool has_data_ = false;
DISALLOW_COPY_AND_ASSIGN(PreParsedScopeData); DISALLOW_COPY_AND_ASSIGN(PreParsedScopeData);
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment