[parser] Skipping inner funcs: Associate data to SharedFunctionInfo, not Script.

This way, each lazy function needs to handle only the data relevant to
itself. This reduced data handling overheads.

Other changes:

1) Don't deserialize the data; once it's on the heap, it can stay there. Lazy
function compilation is only done in the main thread.

2) Separate ProducedPreParsedScopeData and ConsumedPreParsedScopeData. It's clearer, because:

- The data looks fundamentally different when we're producing it and when we're
  consuming it.

- Cleanly separates the operations we can do in the "producing phase" and in the
  "consuming phase".

Bug: v8:5516
Change-Id: I6985a6621f71b348a55155724765624b5d5f7c33
Reviewed-on: https://chromium-review.googlesource.com/528094
Commit-Queue: Marja Hölttä <marja@chromium.org>
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: Adam Klein <adamk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46347}
This commit is contained in:
Marja Hölttä 2017-06-30 12:38:38 +02:00 committed by Commit Bot
parent 27b0d6a9fc
commit 937b5011b8
29 changed files with 974 additions and 560 deletions

View File

@ -121,6 +121,7 @@ class BreakableStatement;
class Expression;
class IterationStatement;
class MaterializedLiteral;
class ProducedPreParsedScopeData;
class Statement;
#define DEF_FORWARD_DECLARATION(type) class type;
@ -2583,19 +2584,23 @@ class FunctionLiteral final : public Expression {
function_literal_id_ = function_literal_id;
}
ProducedPreParsedScopeData* produced_preparsed_scope_data() const {
return produced_preparsed_scope_data_;
}
void ReplaceBodyAndScope(FunctionLiteral* other);
private:
friend class AstNodeFactory;
FunctionLiteral(Zone* zone, const AstRawString* name,
AstValueFactory* ast_value_factory, DeclarationScope* scope,
ZoneList<Statement*>* body, int expected_property_count,
int parameter_count, int function_length,
FunctionType function_type,
ParameterFlag has_duplicate_parameters,
EagerCompileHint eager_compile_hint, int position,
bool has_braces, int function_literal_id)
FunctionLiteral(
Zone* zone, const AstRawString* name, AstValueFactory* ast_value_factory,
DeclarationScope* scope, ZoneList<Statement*>* body,
int expected_property_count, int parameter_count, int function_length,
FunctionType function_type, ParameterFlag has_duplicate_parameters,
EagerCompileHint eager_compile_hint, int position, bool has_braces,
int function_literal_id,
ProducedPreParsedScopeData* produced_preparsed_scope_data = nullptr)
: Expression(position, kFunctionLiteral),
expected_property_count_(expected_property_count),
parameter_count_(parameter_count),
@ -2608,7 +2613,8 @@ class FunctionLiteral final : public Expression {
body_(body),
raw_inferred_name_(ast_value_factory->empty_cons_string()),
ast_properties_(zone),
function_literal_id_(function_literal_id) {
function_literal_id_(function_literal_id),
produced_preparsed_scope_data_(produced_preparsed_scope_data) {
bit_field_ |= FunctionTypeBits::encode(function_type) |
Pretenure::encode(false) |
HasDuplicateParameters::encode(has_duplicate_parameters ==
@ -2644,6 +2650,7 @@ class FunctionLiteral final : public Expression {
AstProperties ast_properties_;
int function_literal_id_;
FeedbackSlot literal_feedback_slot_;
ProducedPreParsedScopeData* produced_preparsed_scope_data_;
};
// Property is used for passing information
@ -3423,12 +3430,13 @@ class AstNodeFactory final BASE_EMBEDDED {
FunctionLiteral::ParameterFlag has_duplicate_parameters,
FunctionLiteral::FunctionType function_type,
FunctionLiteral::EagerCompileHint eager_compile_hint, int position,
bool has_braces, int function_literal_id) {
bool has_braces, int function_literal_id,
ProducedPreParsedScopeData* produced_preparsed_scope_data = nullptr) {
return new (zone_) FunctionLiteral(
zone_, name, ast_value_factory_, scope, body, expected_property_count,
parameter_count, function_length, function_type,
has_duplicate_parameters, eager_compile_hint, position, has_braces,
function_literal_id);
function_literal_id, produced_preparsed_scope_data);
}
// Creates a FunctionLiteral representing a top-level script, the

View File

@ -314,6 +314,7 @@ void DeclarationScope::SetDefaults() {
should_eager_compile_ = false;
was_lazily_parsed_ = false;
is_skipped_function_ = false;
produced_preparsed_scope_data_ = nullptr;
#ifdef DEBUG
DeclarationScope* outer_declaration_scope =
outer_scope_ ? outer_scope_->GetDeclarationScope() : nullptr;
@ -665,9 +666,8 @@ void DeclarationScope::Analyze(ParseInfo* info, Isolate* isolate,
if (scope->must_use_preparsed_scope_data_) {
DCHECK(FLAG_experimental_preparser_scope_analysis);
DCHECK_NOT_NULL(info->preparsed_scope_data());
DCHECK_EQ(scope->scope_type_, ScopeType::FUNCTION_SCOPE);
info->preparsed_scope_data()->RestoreData(scope);
info->consumed_preparsed_scope_data()->RestoreScopeAllocationData(scope);
}
scope->AllocateVariables(info, isolate, mode);
@ -1514,15 +1514,30 @@ void DeclarationScope::ResetAfterPreparsing(AstValueFactory* ast_value_factory,
was_lazily_parsed_ = !aborted;
}
void DeclarationScope::AnalyzePartially(
AstNodeFactory* ast_node_factory,
PreParsedScopeData* preparsed_scope_data) {
void Scope::SavePreParsedScopeData() {
DCHECK(FLAG_experimental_preparser_scope_analysis);
if (ProducedPreParsedScopeData::ScopeIsSkippableFunctionScope(this)) {
AsDeclarationScope()->SavePreParsedScopeDataForDeclarationScope();
}
for (Scope* scope = inner_scope_; scope != nullptr; scope = scope->sibling_) {
scope->SavePreParsedScopeData();
}
}
void DeclarationScope::SavePreParsedScopeDataForDeclarationScope() {
if (produced_preparsed_scope_data_ != nullptr) {
DCHECK(FLAG_experimental_preparser_scope_analysis);
produced_preparsed_scope_data_->SaveScopeAllocationData(this);
}
}
void DeclarationScope::AnalyzePartially(AstNodeFactory* ast_node_factory) {
DCHECK(!force_eager_compilation_);
VariableProxy* unresolved = nullptr;
bool need_preparsed_scope_data = FLAG_experimental_preparser_scope_analysis &&
preparsed_scope_data->Producing();
if (!outer_scope_->is_script_scope() || need_preparsed_scope_data) {
if (!outer_scope_->is_script_scope() ||
FLAG_experimental_preparser_scope_analysis) {
// 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.
@ -1545,12 +1560,11 @@ void DeclarationScope::AnalyzePartially(
function_ = ast_node_factory->CopyVariable(function_);
}
if (need_preparsed_scope_data) {
// Store the information needed for allocating the locals of this scope
// and its inner scopes.
preparsed_scope_data->SaveData(this);
if (FLAG_experimental_preparser_scope_analysis) {
SavePreParsedScopeData();
}
}
#ifdef DEBUG
if (FLAG_print_scopes) {
PrintF("Inner function scope:\n");

View File

@ -21,6 +21,7 @@ class AstRawString;
class Declaration;
class ParseInfo;
class PreParsedScopeData;
class ProducedPreParsedScopeData;
class SloppyBlockFunctionStatement;
class Statement;
class StringSet;
@ -509,6 +510,11 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
Variable* NewTemporary(const AstRawString* name,
MaybeAssignedFlag maybe_assigned);
// Walk the scope chain to find DeclarationScopes; call
// SavePreParsedScopeDataForDeclarationScope for each.
void SavePreParsedScopeData();
Zone* zone_;
// Scope tree.
@ -858,12 +864,11 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
static void Analyze(ParseInfo* info, Isolate* isolate, AnalyzeMode mode);
// To be called during parsing. Do just enough scope analysis that we can
// discard the Scope for lazily compiled functions. In particular, 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
// migrates them into migrate_to.
void AnalyzePartially(AstNodeFactory* ast_node_factory,
PreParsedScopeData* preparsed_scope_data);
// discard the Scope contents for lazily compiled functions. In particular,
// 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);
Handle<StringSet> CollectNonLocals(ParseInfo* info,
Handle<StringSet> non_locals);
@ -897,6 +902,20 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
is_skipped_function_ = is_skipped_function;
}
// 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_preparsed_scope_data_.
void SavePreParsedScopeDataForDeclarationScope();
void set_produced_preparsed_scope_data(
ProducedPreParsedScopeData* produced_preparsed_scope_data) {
produced_preparsed_scope_data_ = produced_preparsed_scope_data;
}
ProducedPreParsedScopeData* produced_preparsed_scope_data() const {
return produced_preparsed_scope_data_;
}
private:
void AllocateParameter(Variable* var, int index);
@ -948,6 +967,9 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
// Convenience variable; function scopes only.
Variable* arguments_;
// For producing the scope allocation data during preparsing.
ProducedPreParsedScopeData* produced_preparsed_scope_data_;
struct RareData : public ZoneObject {
// Convenience variable; Subclass constructor only
Variable* this_function = nullptr;

View File

@ -1008,10 +1008,13 @@ MaybeHandle<Code> GetLazyCode(Handle<JSFunction> function) {
CompilationInfo info(&compile_zone, &parse_info, isolate, function);
if (FLAG_experimental_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->preparsed_scope_data());
if (shared->HasPreParsedScopeData()) {
Handle<PreParsedScopeData> data(
PreParsedScopeData::cast(shared->preparsed_scope_data()));
parse_info.consumed_preparsed_scope_data()->SetData(data);
// After we've compiled the function, we don't need data about its
// skippable functions any more.
shared->set_preparsed_scope_data(isolate->heap()->null_value());
}
}
ConcurrencyMode inner_function_mode = FLAG_compiler_dispatcher_eager_inner
@ -1112,11 +1115,6 @@ Handle<SharedFunctionInfo> CompileToplevel(CompilationInfo* info) {
if (!script.is_null()) {
script->set_compilation_state(Script::COMPILATION_STATE_COMPILED);
if (FLAG_experimental_preparser_scope_analysis) {
Handle<PodArray<uint32_t>> data =
parse_info->preparsed_scope_data()->Serialize(isolate);
script->set_preparsed_scope_data(*data);
}
}
}

View File

@ -327,10 +327,10 @@ Type::bitset BitsetType::Lub(i::Map* map) {
case TUPLE3_TYPE:
case CONTEXT_EXTENSION_TYPE:
case ASYNC_GENERATOR_REQUEST_TYPE:
case PREPARSED_SCOPE_DATA_TYPE:
case PADDING_TYPE_1:
case PADDING_TYPE_2:
case PADDING_TYPE_3:
case PADDING_TYPE_4:
UNREACHABLE();
}
UNREACHABLE();

View File

@ -3979,10 +3979,10 @@ Handle<Object> TranslatedState::MaterializeCapturedObjectAt(
case TUPLE2_TYPE:
case TUPLE3_TYPE:
case ASYNC_GENERATOR_REQUEST_TYPE:
case PREPARSED_SCOPE_DATA_TYPE:
case PADDING_TYPE_1:
case PADDING_TYPE_2:
case PADDING_TYPE_3:
case PADDING_TYPE_4:
OFStream os(stderr);
os << "[couldn't handle instance type " << map->instance_type() << "]"
<< std::endl;

View File

@ -1130,8 +1130,6 @@ Handle<Script> Factory::NewScript(Handle<String> source) {
script->set_eval_from_position(0);
script->set_shared_function_infos(*empty_fixed_array(), SKIP_WRITE_BARRIER);
script->set_flags(0);
script->set_preparsed_scope_data(
PodArray<uint32_t>::cast(heap->empty_byte_array()));
heap->set_script_list(*WeakFixedArray::Add(script_list(), script));
return script;
@ -1667,6 +1665,14 @@ Handle<ModuleInfo> Factory::NewModuleInfo() {
return Handle<ModuleInfo>::cast(array);
}
Handle<PreParsedScopeData> Factory::NewPreParsedScopeData() {
Handle<PreParsedScopeData> result =
Handle<PreParsedScopeData>::cast(NewStruct(PREPARSED_SCOPE_DATA_TYPE));
result->set_scope_data(PodArray<uint32_t>::cast(*empty_byte_array()));
result->set_child_data(*empty_fixed_array());
return result;
}
Handle<JSObject> Factory::NewExternal(void* value) {
Handle<Foreign> foreign = NewForeign(static_cast<Address>(value));
Handle<JSObject> external = NewJSObjectFromMap(external_map());
@ -2430,7 +2436,7 @@ Handle<SharedFunctionInfo> Factory::NewSharedFunctionInfoForLiteral(
Handle<SharedFunctionInfo> result =
NewSharedFunctionInfo(literal->name(), literal->kind(), code, scope_info);
SharedFunctionInfo::InitFromFunctionLiteral(result, literal);
SharedFunctionInfo::SetScript(result, script);
SharedFunctionInfo::SetScript(result, script, false);
return result;
}
@ -2510,6 +2516,8 @@ Handle<SharedFunctionInfo> Factory::NewSharedFunctionInfo(
share->set_compiler_hints(0);
share->set_opt_count_and_bailout_reason(0);
share->set_preparsed_scope_data(*null_value());
// Link into the list.
Handle<Object> new_noscript_list =
WeakFixedArray::Add(noscript_shared_function_infos(), share);

View File

@ -25,6 +25,7 @@ class ConstantElementsPair;
class CoverageInfo;
class DebugInfo;
struct SourceRange;
class PreParsedScopeData;
enum FunctionMode {
// With prototype.
@ -641,6 +642,8 @@ class V8_EXPORT_PRIVATE Factory final {
Handle<ModuleInfoEntry> NewModuleInfoEntry();
Handle<ModuleInfo> NewModuleInfo();
Handle<PreParsedScopeData> NewPreParsedScopeData();
// Create an External object for V8's external API.
Handle<JSObject> NewExternal(void* value);

View File

@ -928,6 +928,7 @@ DEFINE_BOOL(trace_maps, false, "trace map creation")
// preparser.cc
DEFINE_BOOL(use_parse_tasks, false, "use parse tasks")
DEFINE_BOOL(trace_parse_tasks, false, "trace parse task creation")
DEFINE_NEG_IMPLICATION(use_parse_tasks, experimental_preparser_scope_analysis)
// parser.cc
DEFINE_BOOL(allow_natives_syntax, false, "allow natives syntax")

View File

@ -739,6 +739,10 @@ void SharedFunctionInfo::SharedFunctionInfoVerify() {
CHECK(kind() == scope_info()->function_kind());
CHECK_EQ(kind() == kModule, scope_info()->scope_type() == MODULE_SCOPE);
}
CHECK(preparsed_scope_data()->IsNull(isolate) ||
preparsed_scope_data()->IsPreParsedScopeData());
VerifyObjectField(kPreParsedScopeDataOffset);
}
@ -1423,6 +1427,13 @@ void StackFrameInfo::StackFrameInfoVerify() {
VerifyPointer(script_name_or_source_url());
VerifyPointer(function_name());
}
void PreParsedScopeData::PreParsedScopeDataVerify() {
CHECK(IsPreParsedScopeData());
CHECK(scope_data()->IsByteArray());
CHECK(child_data()->IsFixedArray());
}
#endif // VERIFY_HEAP
#ifdef DEBUG

View File

@ -1119,6 +1119,11 @@ void SharedFunctionInfo::SharedFunctionInfoPrint(std::ostream& os) { // NOLINT
os << "\n - length = " << length();
os << "\n - feedback_metadata = ";
feedback_metadata()->FeedbackMetadataPrint(os);
if (HasPreParsedScopeData()) {
os << "\n - preparsed scope data = " << preparsed_scope_data();
} else {
os << "\n - no preparsed scope data";
}
os << "\n";
}
@ -1539,6 +1544,12 @@ void LayoutDescriptor::Print(std::ostream& os) { // NOLINT
os << "\n";
}
void PreParsedScopeData::PreParsedScopeDataPrint(std::ostream& os) { // NOLINT
HeapObject::PrintHeader(os, "PreParsedScopeData");
os << "\n - scope_data: " << Brief(scope_data());
os << "\n - child_data: " << Brief(child_data());
os << "\n";
}
#endif // OBJECT_PRINT

View File

@ -61,6 +61,7 @@
#include "src/objects/frame-array-inl.h"
#include "src/objects/hash-table.h"
#include "src/objects/map.h"
#include "src/parsing/preparsed-scope-data.h"
#include "src/property-descriptor.h"
#include "src/prototype.h"
#include "src/regexp/jsregexp.h"
@ -13013,9 +13014,6 @@ Script::Iterator::Iterator(Isolate* isolate)
Script* Script::Iterator::Next() { return iterator_.Next<Script>(); }
bool Script::HasPreparsedScopeData() const {
return preparsed_scope_data()->length() > 0;
}
SharedFunctionInfo::ScriptIterator::ScriptIterator(Handle<Script> script)
: ScriptIterator(script->GetIsolate(),
@ -13058,13 +13056,17 @@ SharedFunctionInfo* SharedFunctionInfo::GlobalIterator::Next() {
}
}
void SharedFunctionInfo::SetScript(Handle<SharedFunctionInfo> shared,
Handle<Object> script_object) {
Handle<Object> script_object,
bool reset_preparsed_scope_data) {
DCHECK_NE(shared->function_literal_id(), FunctionLiteral::kIdTypeInvalid);
if (shared->script() == *script_object) return;
Isolate* isolate = shared->GetIsolate();
if (reset_preparsed_scope_data) {
shared->set_preparsed_scope_data(isolate->heap()->null_value());
}
// Add shared function info to new script's list. If a collection occurs,
// the shared function info may be temporarily in two lists.
// This is okay because the gc-time processing of these lists can tolerate
@ -13387,11 +13389,24 @@ void SharedFunctionInfo::InitFromFunctionLiteral(
shared_info->set_length(lit->function_length());
shared_info->set_has_duplicate_parameters(lit->has_duplicate_parameters());
shared_info->SetExpectedNofPropertiesFromEstimate(lit);
DCHECK_NULL(lit->produced_preparsed_scope_data());
} else {
// Set an invalid length for lazy functions. This way we can set the correct
// value after compiling, but avoid overwriting values set manually by the
// bootstrapper.
shared_info->set_length(SharedFunctionInfo::kInvalidLength);
if (FLAG_experimental_preparser_scope_analysis) {
ProducedPreParsedScopeData* scope_data =
lit->produced_preparsed_scope_data();
if (scope_data != nullptr) {
MaybeHandle<PreParsedScopeData> maybe_data =
scope_data->Serialize(shared_info->GetIsolate());
if (!maybe_data.is_null()) {
Handle<PreParsedScopeData> data = maybe_data.ToHandleChecked();
shared_info->set_preparsed_scope_data(*data);
}
}
}
}
}

View File

@ -152,6 +152,7 @@
// - PrototypeInfo
// - Module
// - ModuleInfoEntry
// - PreParsedScopeData
// - WeakCell
//
// Formats of Object*:
@ -358,6 +359,7 @@ const int kStubMinorKeyBits = kSmiValueSize - kStubMajorKeyBits - 1;
V(MODULE_TYPE) \
V(MODULE_INFO_ENTRY_TYPE) \
V(ASYNC_GENERATOR_REQUEST_TYPE) \
V(PREPARSED_SCOPE_DATA_TYPE) \
V(FIXED_ARRAY_TYPE) \
V(TRANSITION_ARRAY_TYPE) \
V(SHARED_FUNCTION_INFO_TYPE) \
@ -371,7 +373,6 @@ const int kStubMinorKeyBits = kSmiValueSize - kStubMajorKeyBits - 1;
V(PADDING_TYPE_1) \
V(PADDING_TYPE_2) \
V(PADDING_TYPE_3) \
V(PADDING_TYPE_4) \
\
V(JS_PROXY_TYPE) \
V(JS_GLOBAL_OBJECT_TYPE) \
@ -533,7 +534,8 @@ const int kStubMinorKeyBits = kSmiValueSize - kStubMajorKeyBits - 1;
V(CONTEXT_EXTENSION, ContextExtension, context_extension) \
V(MODULE, Module, module) \
V(MODULE_INFO_ENTRY, ModuleInfoEntry, module_info_entry) \
V(ASYNC_GENERATOR_REQUEST, AsyncGeneratorRequest, async_generator_request)
V(ASYNC_GENERATOR_REQUEST, AsyncGeneratorRequest, async_generator_request) \
V(PREPARSED_SCOPE_DATA, PreParsedScopeData, preparsed_scope_data)
// We use the full 8 bits of the instance_type field to encode heap object
// instance types. The high-order bit (bit 7) is set if the object is not a
@ -702,6 +704,7 @@ enum InstanceType {
MODULE_TYPE,
MODULE_INFO_ENTRY_TYPE,
ASYNC_GENERATOR_REQUEST_TYPE,
PREPARSED_SCOPE_DATA_TYPE,
FIXED_ARRAY_TYPE,
TRANSITION_ARRAY_TYPE,
SHARED_FUNCTION_INFO_TYPE,
@ -716,7 +719,6 @@ enum InstanceType {
PADDING_TYPE_1,
PADDING_TYPE_2,
PADDING_TYPE_3,
PADDING_TYPE_4,
// All the following types are subtypes of JSReceiver, which corresponds to
// objects in the JS sense. The first and the last type in this range are

View File

@ -36,8 +36,6 @@ ACCESSORS(Script, source_url, Object, kSourceUrlOffset)
ACCESSORS(Script, source_mapping_url, Object, kSourceMappingUrlOffset)
ACCESSORS_CHECKED(Script, wasm_compiled_module, Object, kEvalFromSharedOffset,
this->type() == TYPE_WASM)
ACCESSORS(Script, preparsed_scope_data, PodArray<uint32_t>,
kPreParsedScopeDataOffset)
Script::CompilationType Script::compilation_type() {
return BooleanBit::get(flags(), kCompilationTypeBit) ? COMPILATION_TYPE_EVAL

View File

@ -88,8 +88,6 @@ class Script : public Struct {
// This must only be called if the type of this script is TYPE_WASM.
DECL_ACCESSORS(wasm_compiled_module, Object)
DECL_ACCESSORS(preparsed_scope_data, PodArray<uint32_t>)
// [compilation_type]: how the the script was compiled. Encoded in the
// 'flags' field.
inline CompilationType compilation_type();
@ -176,8 +174,6 @@ class Script : public Struct {
DISALLOW_COPY_AND_ASSIGN(Iterator);
};
bool HasPreparsedScopeData() const;
// Dispatched behavior.
DECL_PRINTER(Script)
DECL_VERIFIER(Script)
@ -199,9 +195,7 @@ class Script : public Struct {
static const int kFlagsOffset = kSharedFunctionInfosOffset + kPointerSize;
static const int kSourceUrlOffset = kFlagsOffset + kPointerSize;
static const int kSourceMappingUrlOffset = kSourceUrlOffset + kPointerSize;
static const int kPreParsedScopeDataOffset =
kSourceMappingUrlOffset + kPointerSize;
static const int kSize = kPreParsedScopeDataOffset + kPointerSize;
static const int kSize = kSourceMappingUrlOffset + kPointerSize;
private:
// Bit positions in the flags field.

View File

@ -14,6 +14,10 @@
namespace v8 {
namespace internal {
CAST_ACCESSOR(PreParsedScopeData)
ACCESSORS(PreParsedScopeData, scope_data, PodArray<uint32_t>, kScopeDataOffset)
ACCESSORS(PreParsedScopeData, child_data, FixedArray, kChildDataOffset)
TYPE_CHECKER(SharedFunctionInfo, SHARED_FUNCTION_INFO_TYPE)
CAST_ACCESSOR(SharedFunctionInfo)
DEFINE_DEOPT_ELEMENT_ACCESSORS(SharedFunctionInfo, Object)
@ -29,6 +33,8 @@ ACCESSORS(SharedFunctionInfo, script, Object, kScriptOffset)
ACCESSORS(SharedFunctionInfo, debug_info, Object, kDebugInfoOffset)
ACCESSORS(SharedFunctionInfo, function_identifier, Object,
kFunctionIdentifierOffset)
ACCESSORS(SharedFunctionInfo, preparsed_scope_data, Object,
kPreParsedScopeDataOffset)
BIT_FIELD_ACCESSORS(SharedFunctionInfo, start_position_and_type,
is_named_expression,
@ -385,6 +391,10 @@ bool SharedFunctionInfo::IsSubjectToDebugging() {
return IsUserJavaScript() && !HasAsmWasmData();
}
bool SharedFunctionInfo::HasPreParsedScopeData() const {
return preparsed_scope_data()->IsPreParsedScopeData();
}
} // namespace internal
} // namespace v8

View File

@ -14,6 +14,23 @@
namespace v8 {
namespace internal {
class PreParsedScopeData : public Struct {
public:
DECL_ACCESSORS(scope_data, PodArray<uint32_t>)
DECL_ACCESSORS(child_data, FixedArray)
static const int kScopeDataOffset = Struct::kHeaderSize;
static const int kChildDataOffset = kScopeDataOffset + kPointerSize;
static const int kSize = kChildDataOffset + kPointerSize;
DECL_CAST(PreParsedScopeData)
DECL_PRINTER(PreParsedScopeData)
DECL_VERIFIER(PreParsedScopeData)
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(PreParsedScopeData);
};
// SharedFunctionInfo describes the JSFunction information that can be
// shared by multiple instances of the function.
class SharedFunctionInfo : public HeapObject {
@ -45,8 +62,9 @@ class SharedFunctionInfo : public HeapObject {
// Set up the link between shared function info and the script. The shared
// function info is added to the list on the script.
V8_EXPORT_PRIVATE static void SetScript(Handle<SharedFunctionInfo> shared,
Handle<Object> script_object);
V8_EXPORT_PRIVATE static void SetScript(
Handle<SharedFunctionInfo> shared, Handle<Object> script_object,
bool reset_preparsed_scope_data = true);
// Layout description of the optimized code map.
static const int kEntriesStart = 0;
@ -187,6 +205,11 @@ class SharedFunctionInfo : public HeapObject {
// [debug info]: Debug information.
DECL_ACCESSORS(debug_info, Object)
// PreParsedScopeData or null.
DECL_ACCESSORS(preparsed_scope_data, Object)
inline bool HasPreParsedScopeData() const;
// Bit field containing various information collected for debugging.
// This field is either stored on the kDebugInfo slot or inside the
// debug info struct.
@ -436,6 +459,7 @@ class SharedFunctionInfo : public HeapObject {
V(kDebugInfoOffset, kPointerSize) \
V(kFunctionIdentifierOffset, kPointerSize) \
V(kFeedbackMetadataOffset, kPointerSize) \
V(kPreParsedScopeDataOffset, kPointerSize) \
V(kEndOfPointerFieldsOffset, 0) \
/* Raw data fields. */ \
V(kFunctionLiteralIdOffset, kInt32Size) \

View File

@ -119,7 +119,9 @@ class V8_EXPORT_PRIVATE ParseInfo : public CompileJobFinishCallback {
ScriptData** cached_data() const { return cached_data_; }
void set_cached_data(ScriptData** cached_data) { cached_data_ = cached_data; }
PreParsedScopeData* preparsed_scope_data() { return &preparsed_scope_data_; }
ConsumedPreParsedScopeData* consumed_preparsed_scope_data() {
return &consumed_preparsed_scope_data_;
}
ScriptCompiler::CompileOptions compile_options() const {
return compile_options_;
@ -303,7 +305,7 @@ class V8_EXPORT_PRIVATE ParseInfo : public CompileJobFinishCallback {
//----------- Inputs+Outputs of parsing and scope analysis -----------------
ScriptData** cached_data_; // used if available, populated if requested.
PreParsedScopeData preparsed_scope_data_;
ConsumedPreParsedScopeData consumed_preparsed_scope_data_;
AstValueFactory* ast_value_factory_; // used if available, otherwise new.
const class AstStringConstants* ast_string_constants_;
const AstRawString* function_name_;

View File

@ -20,8 +20,6 @@
namespace v8 {
namespace internal {
class PreParsedScopeData;
enum FunctionNameValidity {
kFunctionNameIsStrictReserved,
kSkipFunctionNameCheck,
@ -246,7 +244,6 @@ class ParserBase {
ParserBase(Zone* zone, Scanner* scanner, uintptr_t stack_limit,
v8::Extension* extension, AstValueFactory* ast_value_factory,
RuntimeCallStats* runtime_call_stats,
PreParsedScopeData* preparsed_scope_data,
bool parsing_on_main_thread = true)
: scope_(nullptr),
original_scope_(nullptr),
@ -259,7 +256,6 @@ class ParserBase {
parsing_on_main_thread_(parsing_on_main_thread),
parsing_module_(false),
stack_limit_(stack_limit),
preparsed_scope_data_(preparsed_scope_data),
zone_(zone),
classifier_(nullptr),
scanner_(scanner),
@ -314,6 +310,10 @@ class ParserBase {
void ResetFunctionLiteralId() { function_literal_id_ = 0; }
// The Zone where the parsing outputs are stored.
Zone* main_zone() const { return ast_value_factory()->zone(); }
// The current Zone, which might be the main zone or a temporary Zone.
Zone* zone() const { return zone_; }
protected:
@ -1555,7 +1555,6 @@ class ParserBase {
bool parsing_on_main_thread_;
bool parsing_module_;
uintptr_t stack_limit_;
PreParsedScopeData* preparsed_scope_data_;
// Parser base's private field members.
@ -4305,6 +4304,7 @@ ParserBase<Impl>::ParseArrowFunctionLiteral(
can_preparse && impl()->AllowsLazyParsingWithoutUnresolvedVariables();
bool should_be_used_once_hint = false;
bool has_braces = true;
ProducedPreParsedScopeData* produced_preparsed_scope_data = nullptr;
{
FunctionState function_state(&function_state_, &scope_,
formal_parameters.scope);
@ -4325,9 +4325,10 @@ ParserBase<Impl>::ParseArrowFunctionLiteral(
DCHECK((kind & FunctionKind::kArrowFunction) != 0);
LazyParsingResult result = impl()->SkipFunction(
nullptr, kind, FunctionLiteral::kAnonymousExpression,
formal_parameters.scope, &dummy_num_parameters, false, false,
CHECK_OK);
formal_parameters.scope, &dummy_num_parameters,
&produced_preparsed_scope_data, false, false, CHECK_OK);
DCHECK_NE(result, kLazyParsingAborted);
DCHECK_NULL(produced_preparsed_scope_data);
USE(result);
formal_parameters.scope->ResetAfterPreparsing(ast_value_factory_,
false);
@ -4413,7 +4414,7 @@ ParserBase<Impl>::ParseArrowFunctionLiteral(
FunctionLiteral::kNoDuplicateParameters,
FunctionLiteral::kAnonymousExpression, eager_compile_hint,
formal_parameters.scope->start_position(), has_braces,
function_literal_id);
function_literal_id, produced_preparsed_scope_data);
function_literal->set_function_token_position(
formal_parameters.scope->start_position());

View File

@ -483,8 +483,7 @@ Expression* Parser::NewV8Intrinsic(const AstRawString* name,
Parser::Parser(ParseInfo* info)
: ParserBase<Parser>(info->zone(), &scanner_, info->stack_limit(),
info->extension(), info->ast_value_factory(),
info->runtime_call_stats(),
info->preparsed_scope_data(), true),
info->runtime_call_stats(), true),
scanner_(info->unicode_cache()),
reusable_preparser_(nullptr),
mode_(PARSE_EAGERLY), // Lazy mode must be set explicitly.
@ -494,6 +493,7 @@ Parser::Parser(ParseInfo* info)
total_preparse_skipped_(0),
temp_zoned_(false),
log_(nullptr),
consumed_preparsed_scope_data_(info->consumed_preparsed_scope_data()),
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
@ -2661,6 +2661,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
int function_length = -1;
bool has_duplicate_parameters = false;
int function_literal_id = GetNextFunctionLiteralId();
ProducedPreParsedScopeData* produced_preparsed_scope_data = nullptr;
Expect(Token::LPAREN, CHECK_OK);
@ -2728,7 +2729,8 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
bookmark.Set();
LazyParsingResult result = SkipFunction(
function_name, kind, function_type, scope, &num_parameters,
is_lazy_inner_function, is_lazy_top_level_function, CHECK_OK);
&produced_preparsed_scope_data, is_lazy_inner_function,
is_lazy_top_level_function, CHECK_OK);
if (result == kLazyParsingAborted) {
DCHECK(is_lazy_top_level_function);
@ -2747,8 +2749,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
}
if (should_preparse) {
scope->AnalyzePartially(&previous_zone_ast_node_factory,
preparsed_scope_data_);
scope->AnalyzePartially(&previous_zone_ast_node_factory);
} else {
body = ParseFunction(function_name, pos, kind, function_type, scope,
&num_parameters, &function_length,
@ -2803,7 +2804,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
FunctionLiteral* function_literal = factory()->NewFunctionLiteral(
function_name, scope, body, expected_property_count, num_parameters,
function_length, duplicate_parameters, function_type, eager_compile_hint,
pos, true, function_literal_id);
pos, true, function_literal_id, produced_preparsed_scope_data);
if (should_use_parse_task) {
literals_to_stitch_.emplace_back(function_literal);
}
@ -2822,6 +2823,7 @@ Parser::LazyParsingResult Parser::SkipFunction(
const AstRawString* function_name, FunctionKind kind,
FunctionLiteral::FunctionType function_type,
DeclarationScope* function_scope, int* num_parameters,
ProducedPreParsedScopeData** produced_preparsed_scope_data,
bool is_inner_function, bool may_abort, bool* ok) {
FunctionState function_state(&function_state_, &scope_, function_scope);
@ -2859,26 +2861,30 @@ Parser::LazyParsingResult Parser::SkipFunction(
}
// FIXME(marja): There are 3 ways to skip functions now. Unify them.
if (preparsed_scope_data_->Consuming()) {
DCHECK_NOT_NULL(consumed_preparsed_scope_data_);
if (consumed_preparsed_scope_data_->HasData()) {
DCHECK(FLAG_experimental_preparser_scope_analysis);
const PreParseData::FunctionData& data =
preparsed_scope_data_->FindSkippableFunction(
function_scope->start_position());
if (data.is_valid()) {
function_scope->set_is_skipped_function(true);
function_scope->outer_scope()->SetMustUsePreParsedScopeData();
int end_position;
LanguageMode language_mode;
int num_inner_functions;
bool uses_super_property;
*produced_preparsed_scope_data =
consumed_preparsed_scope_data_->GetDataForSkippableFunction(
main_zone(), function_scope->start_position(), &end_position,
num_parameters, &num_inner_functions, &uses_super_property,
&language_mode);
function_scope->set_end_position(data.end);
scanner()->SeekForward(data.end - 1);
Expect(Token::RBRACE, CHECK_OK_VALUE(kLazyParsingComplete));
*num_parameters = data.num_parameters;
SetLanguageMode(function_scope, data.language_mode);
if (data.uses_super_property) {
function_scope->RecordSuperPropertyUsage();
}
SkipFunctionLiterals(data.num_inner_functions);
return kLazyParsingComplete;
function_scope->outer_scope()->SetMustUsePreParsedScopeData();
function_scope->set_is_skipped_function(true);
function_scope->set_end_position(end_position);
scanner()->SeekForward(end_position - 1);
Expect(Token::RBRACE, CHECK_OK_VALUE(kLazyParsingComplete));
SetLanguageMode(function_scope, language_mode);
if (uses_super_property) {
function_scope->RecordSuperPropertyUsage();
}
SkipFunctionLiterals(num_inner_functions);
return kLazyParsingComplete;
}
// With no cached data, we partially parse the function, without building an
@ -2891,7 +2897,7 @@ Parser::LazyParsingResult Parser::SkipFunction(
PreParser::PreParseResult result = reusable_preparser()->PreParseFunction(
function_name, kind, function_type, function_scope, parsing_module_,
is_inner_function, may_abort, use_counts_);
is_inner_function, may_abort, use_counts_, produced_preparsed_scope_data);
// Return immediately if pre-parser decided to abort parsing.
if (result == PreParser::kPreParseAbort) return kLazyParsingAborted;

View File

@ -23,6 +23,7 @@ class ScriptCompiler;
namespace internal {
class ConsumedPreParsedScopeData;
class ParseInfo;
class ScriptData;
class ParserTarget;
@ -288,7 +289,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
reusable_preparser_ =
new PreParser(zone(), &scanner_, stack_limit_, ast_value_factory(),
&pending_error_handler_, runtime_call_stats_,
preparsed_scope_data_, parsing_on_main_thread_);
parsing_on_main_thread_);
#define SET_ALLOW(name) reusable_preparser_->set_allow_##name(allow_##name());
SET_ALLOW(natives);
SET_ALLOW(tailcalls);
@ -555,12 +556,12 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
// by parsing the function with PreParser. Consumes the ending }.
// If may_abort == true, the (pre-)parser may decide to abort skipping
// in order to force the function to be eagerly parsed, after all.
LazyParsingResult SkipFunction(const AstRawString* function_name,
FunctionKind kind,
FunctionLiteral::FunctionType function_type,
DeclarationScope* function_scope,
int* num_parameters, bool is_inner_function,
bool may_abort, bool* ok);
LazyParsingResult SkipFunction(
const AstRawString* function_name, FunctionKind kind,
FunctionLiteral::FunctionType function_type,
DeclarationScope* function_scope, int* num_parameters,
ProducedPreParsedScopeData** produced_preparsed_scope_data,
bool is_inner_function, bool may_abort, bool* ok);
Block* BuildParameterInitializationBlock(
const ParserFormalParameters& parameters, bool* ok);
@ -1173,6 +1174,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
bool allow_lazy_;
bool temp_zoned_;
ParserLogger* log_;
ConsumedPreParsedScopeData* consumed_preparsed_scope_data_;
// If not kNoSourcePosition, indicates that the first function literal
// encountered is a dynamic function, see CreateDynamicFunction(). This field

View File

@ -8,6 +8,8 @@
#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 {
@ -20,37 +22,258 @@ class VariableMaybeAssignedField
class VariableContextAllocatedField
: public BitField16<bool, VariableMaybeAssignedField::kNext, 1> {};
const int kFunctionDataSize = 8;
const int kMagicValue = 0xc0de0de;
enum SkippableFunctionDataOffsets {
kStartPosition,
kEndPosition,
kNumParameters,
kNumInnerFunctions,
kLanguageAndSuper,
kSize
};
STATIC_ASSERT(LANGUAGE_END == 2);
class LanguageField : public BitField<int, 0, 1> {};
class UsesSuperField : public BitField<bool, LanguageField::kNext, 1> {};
} // namespace
/*
Internal data format for the backing store:
Internal data format for the backing store of ProducedPreparsedScopeData:
(Skippable function data:)
------------------------------------
| data for inner function 1 |
| ... |
------------------------------------
| data for inner function n |
| ... |
------------------------------------
(Scope allocation data:)
------------------------------------
magic value
------------------------------------
scope positions
------------------------------------
| scope type << only in debug |
| inner_scope_calls_eval_ |
| data end index |
| ---------------------- |
| | data for variables | |
| | ... | |
| ---------------------- |
------------------------------------
------------------------------------
| data for inner scope_1 |
| data for inner scope 1 | << but not for function scopes
| ... |
------------------------------------
...
------------------------------------
| data for inner scope_n |
| data for inner scope m |
| ... |
------------------------------------
<< data end index points here
Data format for PreParsedScopeData (on the heap):
PreParsedScopeData::scope_data:
------------------------------------
| scope_data_start |
------------------------------------
| Skippable function data |
| (see above) |
| ... |
------------------------------------
------------------------------------
| Scope allocation data | << scope_data_start points here
| (see above) |
| ... |
------------------------------------
PreParsedScopeData::child_data is an array of PreParsedScopeData objects, one
for each skippable inner function.
ConsumedPreParsedScopeData wraps a PreParsedScopeData and reads data from it.
*/
void PreParsedScopeData::SaveData(Scope* scope) {
DCHECK(!has_data_);
ProducedPreParsedScopeData::DataGatheringScope::DataGatheringScope(
DeclarationScope* function_scope, PreParser* preparser)
: function_scope_(function_scope),
preparser_(preparser),
parent_data_(preparser->produced_preparsed_scope_data()) {
if (FLAG_experimental_preparser_scope_analysis) {
Zone* main_zone = preparser->main_zone();
auto* new_data = new (main_zone) ProducedPreParsedScopeData(main_zone);
if (parent_data_ != nullptr) {
parent_data_->data_for_inner_functions_.push_back(new_data);
}
preparser->set_produced_preparsed_scope_data(new_data);
function_scope->set_produced_preparsed_scope_data(new_data);
}
}
ProducedPreParsedScopeData::DataGatheringScope::~DataGatheringScope() {
if (FLAG_experimental_preparser_scope_analysis) {
preparser_->set_produced_preparsed_scope_data(parent_data_);
}
}
void ProducedPreParsedScopeData::DataGatheringScope::MarkFunctionAsSkippable(
int end_position, int num_inner_functions) {
DCHECK(FLAG_experimental_preparser_scope_analysis);
DCHECK_NOT_NULL(parent_data_);
parent_data_->AddSkippableFunction(
function_scope_->start_position(), end_position,
function_scope_->num_parameters(), num_inner_functions,
function_scope_->language_mode(), function_scope_->uses_super_property());
}
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_experimental_preparser_scope_analysis);
DCHECK_EQ(scope_data_start_, -1);
DCHECK(previously_produced_preparsed_scope_data_.is_null());
size_t current_size = backing_store_.size();
backing_store_.resize(current_size + SkippableFunctionDataOffsets::kSize);
backing_store_[current_size + SkippableFunctionDataOffsets::kStartPosition] =
start_position;
backing_store_[current_size + SkippableFunctionDataOffsets::kEndPosition] =
end_position;
backing_store_[current_size + SkippableFunctionDataOffsets::kNumParameters] =
num_parameters;
backing_store_[current_size +
SkippableFunctionDataOffsets::kNumInnerFunctions] =
num_inner_functions;
uint32_t language_and_super = LanguageField::encode(language_mode) |
UsesSuperField::encode(uses_super_property);
backing_store_[current_size +
SkippableFunctionDataOffsets::kLanguageAndSuper] =
language_and_super;
}
void ProducedPreParsedScopeData::SaveScopeAllocationData(
DeclarationScope* scope) {
DCHECK(FLAG_experimental_preparser_scope_analysis);
DCHECK(previously_produced_preparsed_scope_data_.is_null());
DCHECK_EQ(scope_data_start_, -1);
DCHECK_EQ(backing_store_.size() % SkippableFunctionDataOffsets::kSize, 0);
scope_data_start_ = static_cast<int>(backing_store_.size());
// If there are no skippable inner functions, we don't need to save anything.
if (backing_store_.size() == 0) {
return;
}
// For sanity checks.
backing_store_.push_back(kMagicValue);
backing_store_.push_back(scope->start_position());
backing_store_.push_back(scope->end_position());
// For a data integrity check, write a value between data about skipped inner
// funcs and data about variables.
SaveDataForScope(scope);
}
MaybeHandle<PreParsedScopeData> ProducedPreParsedScopeData::Serialize(
Isolate* isolate) const {
if (!previously_produced_preparsed_scope_data_.is_null()) {
DCHECK_EQ(backing_store_.size(), 0);
DCHECK_EQ(data_for_inner_functions_.size(), 0);
return previously_produced_preparsed_scope_data_;
}
// FIXME(marja): save space by using a byte array and converting
// function data to bytes.
size_t length = backing_store_.size();
if (length == 0) {
return MaybeHandle<PreParsedScopeData>();
}
Handle<PodArray<uint32_t>> data_array =
PodArray<uint32_t>::New(isolate, static_cast<int>(length + 1), TENURED);
DCHECK_GE(scope_data_start_, 0);
data_array->set(0, scope_data_start_);
{
int i = 1;
for (const auto& item : backing_store_) {
data_array->set(i++, item);
}
}
Handle<PreParsedScopeData> data = isolate->factory()->NewPreParsedScopeData();
int child_data_length = static_cast<int>(data_for_inner_functions_.size());
if (child_data_length == 0) {
data->set_child_data(*(isolate->factory()->empty_fixed_array()));
} else {
Handle<FixedArray> child_array =
isolate->factory()->NewFixedArray(child_data_length, TENURED);
int i = 0;
for (const auto& item : data_for_inner_functions_) {
MaybeHandle<PreParsedScopeData> maybe_child_data =
item->Serialize(isolate);
if (maybe_child_data.is_null()) {
child_array->set(i++, *(isolate->factory()->null_value()));
} else {
Handle<PreParsedScopeData> child_data =
maybe_child_data.ToHandleChecked();
child_array->set(i++, *child_data);
}
}
data->set_child_data(*child_array);
}
data->set_scope_data(*data_array);
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);
// We're not trying to save data for default constructors because the
@ -59,14 +282,6 @@ void PreParsedScopeData::SaveData(Scope* scope) {
(scope->AsDeclarationScope()->function_kind() &
kDefaultConstructor) == 0);
if (scope->scope_type() == ScopeType::FUNCTION_SCOPE &&
!scope->AsDeclarationScope()->is_arrow_scope()) {
// This cast is OK since we're not going to have more than 2^32 elements in
// the data. FIXME(marja): Implement limits for the data size.
function_data_positions_[scope->start_position()] =
static_cast<uint32_t>(backing_store_.size());
}
if (!ScopeNeedsData(scope)) {
return;
}
@ -75,11 +290,6 @@ void PreParsedScopeData::SaveData(Scope* scope) {
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(0);
if (scope->scope_type() == ScopeType::FUNCTION_SCOPE) {
Variable* function = scope->AsDeclarationScope()->function_var();
@ -87,6 +297,7 @@ void PreParsedScopeData::SaveData(Scope* scope) {
SaveDataForVariable(function);
}
}
for (Variable* var : *scope->locals()) {
if (IsDeclaredVariableMode(var->mode())) {
SaveDataForVariable(var);
@ -94,172 +305,9 @@ void PreParsedScopeData::SaveData(Scope* scope) {
}
SaveDataForInnerScopes(scope);
// FIXME(marja): see above.
backing_store_[data_end_index] = static_cast<uint32_t>(backing_store_.size());
}
void PreParsedScopeData::AddSkippableFunction(
int start_position, const PreParseData::FunctionData& function_data) {
AddFunction(start_position, function_data);
skippable_functions_.insert(start_position);
}
void PreParsedScopeData::AddFunction(
int start_position, const PreParseData::FunctionData& function_data) {
DCHECK(function_data.is_valid());
function_index_.AddFunctionData(start_position, function_data);
}
void PreParsedScopeData::RestoreData(DeclarationScope* scope) const {
uint32_t index = 0;
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, uint32_t* 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;
}
uint32_t& index = *index_ptr;
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());
// Check that we're moving forward (not backward) in the data.
DCHECK_GT(backing_store_[index + 2], index);
index = backing_store_[index + 2];
return;
}
#ifdef DEBUG
// Data integrity check.
if (scope->scope_type() == ScopeType::FUNCTION_SCOPE &&
!scope->AsDeclarationScope()->is_arrow_scope()) {
const PreParseData::FunctionData& data =
function_index_.GetFunctionData(scope->start_position());
DCHECK(data.is_valid());
DCHECK_EQ(data.end, scope->end_position());
DCHECK_EQ(data.num_parameters, scope->num_parameters());
DCHECK_EQ(data.language_mode, scope->language_mode());
DCHECK_EQ(data.uses_super_property,
scope->AsDeclarationScope()->uses_super_property());
uint32_t index_from_data = 0;
DCHECK(FindFunctionData(scope->start_position(), &index_from_data));
DCHECK_EQ(index_from_data, index);
}
#endif
DCHECK_GE(backing_store_.size(), index + 3);
DCHECK_EQ(backing_store_[index++], scope->scope_type());
if (backing_store_[index++]) {
scope->RecordEvalCall();
}
uint32_t data_end_index = backing_store_[index++];
USE(data_end_index);
if (scope->scope_type() == ScopeType::FUNCTION_SCOPE) {
Variable* function = scope->AsDeclarationScope()->function_var();
if (function != nullptr) {
RestoreDataForVariable(function, index_ptr);
}
}
for (Variable* var : *scope->locals()) {
if (var->mode() == VAR || var->mode() == LET || var->mode() == CONST) {
RestoreDataForVariable(var, index_ptr);
}
}
RestoreDataForInnerScopes(scope, index_ptr);
DCHECK_EQ(data_end_index, index);
}
Handle<PodArray<uint32_t>> PreParsedScopeData::Serialize(
Isolate* isolate) const {
// FIXME(marja): save space by using a byte array and converting
// function_index_ to bytes.
size_t length =
function_index_.size() * kFunctionDataSize + backing_store_.size() + 1;
Handle<PodArray<uint32_t>> array =
PodArray<uint32_t>::New(isolate, static_cast<int>(length), TENURED);
array->set(0, static_cast<uint32_t>(function_index_.size()));
int i = 1;
for (const auto& item : function_index_) {
const auto& it = function_data_positions_.find(item.first);
DCHECK(it != function_data_positions_.end());
const PreParseData::FunctionData& function_data = item.second;
array->set(i++, item.first); // start position
array->set(i++, it->second); // position in data
array->set(i++, function_data.end);
array->set(i++, function_data.num_parameters);
array->set(i++, function_data.num_inner_functions);
array->set(i++, function_data.language_mode);
array->set(i++, function_data.uses_super_property);
array->set(i++, skippable_functions_.find(item.first) !=
skippable_functions_.end());
}
for (size_t j = 0; j < backing_store_.size(); ++j) {
array->set(i++, static_cast<uint32_t>(backing_store_[j]));
}
DCHECK_EQ(array->length(), length);
return array;
}
void PreParsedScopeData::Deserialize(PodArray<uint32_t>* array) {
has_data_ = true;
DCHECK_NOT_NULL(array);
if (array->length() == 0) {
return;
}
int function_count = array->get(0);
CHECK(array->length() > function_count * kFunctionDataSize);
if (function_count == 0) {
return;
}
int i = 1;
for (; i < function_count * kFunctionDataSize + 1; i += kFunctionDataSize) {
int start = array->get(i);
function_data_positions_[start] = array->get(i + 1);
function_index_.AddFunctionData(
start, PreParseData::FunctionData(
array->get(i + 2), array->get(i + 3), array->get(i + 4),
LanguageMode(array->get(i + 5)), array->get(i + 6)));
if (array->get(i + 7)) {
skippable_functions_.insert(start);
}
}
CHECK_EQ(function_index_.size(), function_count);
backing_store_.reserve(array->length() - i);
for (; i < array->length(); ++i) {
backing_store_.push_back(array->get(i));
}
}
PreParseData::FunctionData PreParsedScopeData::FindSkippableFunction(
int start_pos) const {
if (skippable_functions_.find(start_pos) == skippable_functions_.end()) {
return PreParseData::FunctionData();
}
return function_index_.GetFunctionData(start_pos);
}
void PreParsedScopeData::SaveDataForVariable(Variable* var) {
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.
@ -279,19 +327,165 @@ void PreParsedScopeData::SaveDataForVariable(Variable* var) {
backing_store_.push_back(variable_data);
}
void PreParsedScopeData::RestoreDataForVariable(Variable* var,
uint32_t* index_ptr) const {
uint32_t& index = *index_ptr;
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(inner->AsDeclarationScope()->produced_preparsed_scope_data() !=
nullptr);
continue;
}
scopes.push_back(inner);
}
for (auto it = scopes.rbegin(); it != scopes.rend(); ++it) {
SaveDataForScope(*it);
}
}
void ConsumedPreParsedScopeData::SetData(Handle<PreParsedScopeData> data) {
DCHECK(data->IsPreParsedScopeData());
data_ = data;
#ifdef DEBUG
DisallowHeapAllocation no_gc;
PodArray<uint32_t>* scope_data = data_->scope_data();
DCHECK_GT(scope_data->length(), 2);
DCHECK_EQ(scope_data->get(scope_data->get(0) + 1), kMagicValue);
#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) {
DisallowHeapAllocation no_gc;
PodArray<uint32_t>* scope_data = data_->scope_data();
// The skippable function *must* be the next function in the data. Use the
// start position as a sanity check.
CHECK_GE(scope_data->length(), index_ + 5);
int start_position_from_data =
scope_data->get(index_ + SkippableFunctionDataOffsets::kStartPosition);
CHECK_EQ(start_position, start_position_from_data);
*end_position =
scope_data->get(index_ + SkippableFunctionDataOffsets::kEndPosition);
DCHECK_GT(*end_position, start_position);
*num_parameters =
scope_data->get(index_ + SkippableFunctionDataOffsets::kNumParameters);
*num_inner_functions = scope_data->get(
index_ + SkippableFunctionDataOffsets::kNumInnerFunctions);
int language_and_super =
scope_data->get(index_ + SkippableFunctionDataOffsets::kLanguageAndSuper);
*language_mode = LanguageMode(LanguageField::decode(language_and_super));
*uses_super_property = UsesSuperField::decode(language_and_super);
index_ += SkippableFunctionDataOffsets::kSize;
// Retrieve the corresponding PreParsedScopeData and associate it to the
// skipped function. If the skipped functions contains inner functions, those
// can be skipped when the skipped function is eagerly parsed.
FixedArray* children = data_->child_data();
CHECK_GT(children->length(), child_index_);
Object* child_data = children->get(child_index_++);
if (!child_data->IsPreParsedScopeData()) {
return nullptr;
}
Handle<PreParsedScopeData> child_data_handle(
PreParsedScopeData::cast(child_data));
return new (zone) ProducedPreParsedScopeData(child_data_handle, zone);
}
void ConsumedPreParsedScopeData::RestoreScopeAllocationData(
DeclarationScope* scope) {
DCHECK(FLAG_experimental_preparser_scope_analysis);
DCHECK_EQ(scope->scope_type(), ScopeType::FUNCTION_SCOPE);
DCHECK(!data_.is_null());
DisallowHeapAllocation no_gc;
PodArray<uint32_t>* scope_data = data_->scope_data();
int magic_value_from_data = scope_data->get(index_++);
// Check that we've consumed all inner function data.
CHECK_EQ(magic_value_from_data, kMagicValue);
int start_position_from_data = scope_data->get(index_++);
int end_position_from_data = scope_data->get(index_++);
CHECK_EQ(start_position_from_data, scope->start_position());
CHECK_EQ(end_position_from_data, scope->end_position());
RestoreData(scope, scope_data);
// Check that we consumed all scope data.
DCHECK_EQ(index_, scope_data->length());
}
void ConsumedPreParsedScopeData::SkipFunctionDataForTesting() {
DCHECK_EQ(index_, 1);
DisallowHeapAllocation no_gc;
PodArray<uint32_t>* scope_data = data_->scope_data();
DCHECK_GT(scope_data->length(), 2);
index_ = scope_data->get(0) + 1;
DCHECK_EQ(scope_data->get(index_), kMagicValue);
}
void ConsumedPreParsedScopeData::RestoreData(Scope* scope,
PodArray<uint32_t>* scope_data) {
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;
}
// scope_type is stored only in debug mode.
CHECK_GE(scope_data->length(), index_ + 1);
DCHECK_GE(scope_data->length(), index_ + 2);
DCHECK_EQ(scope_data->get(index_++), scope->scope_type());
if (scope_data->get(index_++)) {
scope->RecordEvalCall();
}
if (scope->scope_type() == ScopeType::FUNCTION_SCOPE) {
Variable* function = scope->AsDeclarationScope()->function_var();
if (function != nullptr) {
RestoreDataForVariable(function, scope_data);
}
}
for (Variable* var : *scope->locals()) {
if (IsDeclaredVariableMode(var->mode())) {
RestoreDataForVariable(var, scope_data);
}
}
RestoreDataForInnerScopes(scope, scope_data);
}
void ConsumedPreParsedScopeData::RestoreDataForVariable(
Variable* var, PodArray<uint32_t>* scope_data) {
#ifdef DEBUG
const AstRawString* name = var->raw_name();
DCHECK_GT(backing_store_.size(), index + name->length());
DCHECK_EQ(backing_store_[index++], static_cast<uint32_t>(name->length()));
DCHECK_GT(scope_data->length(), index_ + name->length());
DCHECK_EQ(scope_data->get(index_++), static_cast<uint32_t>(name->length()));
for (int i = 0; i < name->length(); ++i) {
DCHECK_EQ(backing_store_[index++], name->raw_data()[i]);
DCHECK_EQ(scope_data->get(index_++), name->raw_data()[i]);
}
#endif
DCHECK_GT(backing_store_.size(), index);
byte variable_data = backing_store_[index++];
CHECK_GT(scope_data->length(), index_);
byte variable_data = scope_data->get(index_++);
if (VariableIsUsedField::decode(variable_data)) {
var->set_is_used();
}
@ -303,68 +497,17 @@ 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.
void ConsumedPreParsedScopeData::RestoreDataForInnerScopes(
Scope* scope, PodArray<uint32_t>* scope_data) {
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]);
for (auto it = scopes.rbegin(); it != scopes.rend(); ++it) {
RestoreData(*it, scope_data);
}
}
void PreParsedScopeData::RestoreDataForInnerScopes(Scope* scope,
uint32_t* 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,
uint32_t* index) const {
auto it = function_data_positions_.find(start_pos);
if (it == function_data_positions_.end()) {
return false;
}
*index = it->second;
return true;
}
bool PreParsedScopeData::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 (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 v8

View File

@ -10,8 +10,10 @@
#include <vector>
#include "src/globals.h"
#include "src/objects.h"
#include "src/handles.h"
#include "src/objects/shared-function-info.h"
#include "src/parsing/preparse-data.h"
#include "src/zone/zone-containers.h"
namespace v8 {
namespace internal {
@ -19,6 +21,9 @@ namespace internal {
template <typename T>
class Handle;
class PreParser;
class PreParsedScopeData;
/*
Skipping inner functions.
@ -53,77 +58,123 @@ class Handle;
For each Scope:
- inner_scope_calls_eval_.
PreParsedScopeData implements storing and restoring the above mentioned data.
ProducedPreParsedScopeData implements storing the above mentioned data and
ConsumedPreParsedScopeData implements restoring it (= setting the context
allocation status of the variables in a Scope (and its subscopes) based on the
data).
*/
class PreParsedScopeData {
class ProducedPreParsedScopeData : public ZoneObject {
public:
PreParsedScopeData() {}
~PreParsedScopeData() {}
// Create a ProducedPreParsedScopeData object which will collect data as we
// parse.
explicit ProducedPreParsedScopeData(Zone* zone)
: backing_store_(zone),
data_for_inner_functions_(zone),
scope_data_start_(-1) {}
// Create a ProducedPreParsedScopeData which is just a proxy for a previous
// produced PreParsedScopeData.
ProducedPreParsedScopeData(Handle<PreParsedScopeData> data, Zone* zone)
: backing_store_(zone),
data_for_inner_functions_(zone),
scope_data_start_(-1),
previously_produced_preparsed_scope_data_(data) {}
// For gathering the inner function data and splitting it up according to the
// laziness boundaries. Each lazy function gets its own
// ProducedPreParsedScopeData, and so do all lazy functions inside it.
class DataGatheringScope {
public:
DataGatheringScope(DeclarationScope* function_scope, PreParser* preparser);
~DataGatheringScope();
void MarkFunctionAsSkippable(int end_position, int num_inner_functions);
private:
DeclarationScope* function_scope_;
PreParser* preparser_;
ProducedPreParsedScopeData* parent_data_;
DISALLOW_COPY_AND_ASSIGN(DataGatheringScope);
};
// Saves the information needed for allocating the Scope's (and its
// subscopes') variables.
void SaveData(Scope* scope);
void SaveScopeAllocationData(DeclarationScope* scope);
// Save data for a function we might skip later. The data is used later for
// creating a FunctionLiteral.
void AddSkippableFunction(int start_position,
const PreParseData::FunctionData& function_data);
// Save variable allocation data for function which contains skippable
// functions.
void AddFunction(int start_position,
const PreParseData::FunctionData& function_data);
// FIXME(marja): We need different kinds of data for the two types of
// functions. For a skippable function we need the end position + the data
// needed for creating a FunctionLiteral. For a function which contains
// skippable functions, we need the data affecting context allocation status
// of the variables (but e.g., no end position). Currently we just save the
// same data for both. Here we can save less data.
// Restores the information needed for allocating the Scopes's (and its
// subscopes') variables.
void RestoreData(Scope* scope, uint32_t* index_ptr) const;
void RestoreData(DeclarationScope* scope) const;
Handle<PodArray<uint32_t>> Serialize(Isolate* isolate) const;
void Deserialize(PodArray<uint32_t>* array);
bool Consuming() const { return has_data_; }
bool Producing() const { return !has_data_; }
PreParseData::FunctionData FindSkippableFunction(int start_pos) const;
private:
friend class ScopeTestHelper;
void SaveDataForVariable(Variable* var);
void RestoreDataForVariable(Variable* var, uint32_t* index_ptr) const;
void SaveDataForInnerScopes(Scope* scope);
void RestoreDataForInnerScopes(Scope* scope, uint32_t* index_ptr) const;
bool FindFunctionData(int start_pos, uint32_t* index) const;
// If there is data (if the Scope contains skippable inner functions), move
// the data into the heap and return a Handle to it; otherwise return a null
// MaybeHandle.
MaybeHandle<PreParsedScopeData> Serialize(Isolate* isolate) const;
static bool ScopeNeedsData(Scope* scope);
static bool IsSkippedFunctionScope(Scope* scope);
static bool ScopeIsSkippableFunctionScope(Scope* scope);
private:
void AddSkippableFunction(int start_position, int end_position,
int num_parameters, int num_inner_functions,
LanguageMode language_mode,
bool uses_super_property);
void SaveDataForScope(Scope* scope);
void SaveDataForVariable(Variable* var);
void SaveDataForInnerScopes(Scope* scope);
// TODO(marja): Make the backing store more efficient once we know exactly
// what data is needed.
std::vector<uint32_t> backing_store_;
ZoneDeque<uint32_t> backing_store_;
ZoneDeque<ProducedPreParsedScopeData*> data_for_inner_functions_;
// The backing store contains data about inner functions and then data about
// this scope's (and its subscopes') variables. scope_data_start_ marks where
// the latter starts.
int scope_data_start_;
// Start pos -> FunctionData. Used for creating FunctionLiterals for skipped
// functions (when they're actually skipped).
PreParseData function_index_;
// Start pos -> position in backing_store_.
std::unordered_map<uint32_t, uint32_t> function_data_positions_;
// Start positions of skippable functions.
std::set<uint32_t> skippable_functions_;
// ProducedPreParsedScopeData can also mask a Handle<PreParsedScopeData>
// which was produced already earlier. This happens for deeper lazy functions.
Handle<PreParsedScopeData> previously_produced_preparsed_scope_data_;
bool has_data_ = false;
DISALLOW_COPY_AND_ASSIGN(ProducedPreParsedScopeData);
};
DISALLOW_COPY_AND_ASSIGN(PreParsedScopeData);
class ConsumedPreParsedScopeData {
public:
// Real data starts from index 1 (see data format description in the .cc
// file).
ConsumedPreParsedScopeData() : index_(1), child_index_(0) {}
~ConsumedPreParsedScopeData() {}
void SetData(Handle<PreParsedScopeData> data);
bool HasData() const { return !data_.is_null(); }
ProducedPreParsedScopeData* GetDataForSkippableFunction(
Zone* zone, int start_position, int* end_position, int* num_parameters,
int* num_inner_functions, bool* uses_super_property,
LanguageMode* language_mode);
// Restores the information needed for allocating the Scope's (and its
// subscopes') variables.
void RestoreScopeAllocationData(DeclarationScope* scope);
// Skips the data about skippable functions, moves straight to the scope
// allocation data. Useful for tests which don't want to verify only the scope
// allocation data.
void SkipFunctionDataForTesting();
private:
void RestoreData(Scope* scope, PodArray<uint32_t>* scope_data);
void RestoreDataForVariable(Variable* var, PodArray<uint32_t>* scope_data);
void RestoreDataForInnerScopes(Scope* scope, PodArray<uint32_t>* scope_data);
Handle<PreParsedScopeData> data_;
// When consuming the data, these indexes point to the data we're going to
// consume next.
int index_;
int child_index_;
DISALLOW_COPY_AND_ASSIGN(ConsumedPreParsedScopeData);
};
} // namespace internal

View File

@ -134,17 +134,28 @@ PreParser::PreParseResult PreParser::PreParseFunction(
const AstRawString* function_name, FunctionKind kind,
FunctionLiteral::FunctionType function_type,
DeclarationScope* function_scope, bool parsing_module,
bool is_inner_function, bool may_abort, int* use_counts) {
bool is_inner_function, bool may_abort, int* use_counts,
ProducedPreParsedScopeData** produced_preparsed_scope_data) {
DCHECK_EQ(FUNCTION_SCOPE, function_scope->scope_type());
parsing_module_ = parsing_module;
use_counts_ = use_counts;
DCHECK(!track_unresolved_variables_);
track_unresolved_variables_ =
is_inner_function || FLAG_experimental_preparser_scope_analysis;
track_unresolved_variables_ = is_inner_function;
#ifdef DEBUG
function_scope->set_is_being_lazily_parsed(true);
#endif
// Start collecting data for a new function which might contain skippable
// functions.
std::unique_ptr<ProducedPreParsedScopeData::DataGatheringScope>
produced_preparsed_scope_data_scope;
if (FLAG_experimental_preparser_scope_analysis && !IsArrowFunction(kind)) {
track_unresolved_variables_ = true;
produced_preparsed_scope_data_scope.reset(
new ProducedPreParsedScopeData::DataGatheringScope(function_scope,
this));
}
// In the preparser, we use the function literal ids to count how many
// FunctionLiterals were encountered. The PreParser doesn't actually persist
// FunctionLiterals, so there IDs don't matter.
@ -216,18 +227,6 @@ PreParser::PreParseResult PreParser::PreParseFunction(
// masks the arguments object. Declare arguments before declaring the
// function var since the arguments object masks 'function arguments'.
function_scope->DeclareArguments(ast_value_factory());
if (FLAG_experimental_preparser_scope_analysis &&
preparsed_scope_data_ != nullptr && result != kLazyParsingAborted) {
// We're not going to skip this function, but it might contain skippable
// functions inside it.
preparsed_scope_data_->AddFunction(
scope()->start_position(),
PreParseData::FunctionData(
scanner()->peek_location().end_pos, scope()->num_parameters(),
GetLastFunctionLiteralId(), scope()->language_mode(),
scope()->AsDeclarationScope()->uses_super_property()));
}
}
use_counts_ = nullptr;
@ -251,6 +250,8 @@ PreParser::PreParseResult PreParser::PreParseFunction(
ValidateFormalParameters(function_scope->language_mode(),
allow_duplicate_parameters,
CHECK_OK_VALUE(kPreParseSuccess));
*produced_preparsed_scope_data = produced_preparsed_scope_data_;
}
if (is_strict(function_scope->language_mode())) {
@ -293,6 +294,20 @@ PreParser::Expression PreParser::ParseFunctionLiteral(
DeclarationScope* function_scope = NewFunctionScope(kind);
function_scope->SetLanguageMode(language_mode);
// Start collecting data for a new function which might contain skippable
// functions.
std::unique_ptr<ProducedPreParsedScopeData::DataGatheringScope>
produced_preparsed_scope_data_scope;
if (!function_state_->next_function_is_likely_called() &&
produced_preparsed_scope_data_ != nullptr) {
DCHECK(FLAG_experimental_preparser_scope_analysis);
DCHECK(track_unresolved_variables_);
produced_preparsed_scope_data_scope.reset(
new ProducedPreParsedScopeData::DataGatheringScope(function_scope,
this));
}
FunctionState function_state(&function_state_, &scope_, function_scope);
DuplicateFinder duplicate_finder;
ExpressionClassifier formals_classifier(this, &duplicate_finder);
@ -338,14 +353,9 @@ PreParser::Expression PreParser::ParseFunctionLiteral(
CheckStrictOctalLiteral(start_position, end_position, CHECK_OK);
}
if (FLAG_experimental_preparser_scope_analysis &&
track_unresolved_variables_ && preparsed_scope_data_ != nullptr) {
preparsed_scope_data_->AddSkippableFunction(
start_position,
PreParseData::FunctionData(
end_position, scope()->num_parameters(),
GetLastFunctionLiteralId() - func_id, scope()->language_mode(),
scope()->AsDeclarationScope()->uses_super_property()));
if (produced_preparsed_scope_data_scope) {
produced_preparsed_scope_data_scope->MarkFunctionAsSkippable(
end_position, GetLastFunctionLiteralId() - func_id);
}
if (FLAG_trace_preparse) {
PrintF(" [%s]: %i-%i\n",

View File

@ -22,6 +22,8 @@ namespace internal {
// interface as AstNodeFactory, so ParserBase doesn't need to care which one is
// used.
class ProducedPreParsedScopeData;
class PreParserIdentifier {
public:
PreParserIdentifier() : type_(kUnknownIdentifier) {}
@ -691,7 +693,9 @@ class PreParserFactory {
FunctionLiteral::ParameterFlag has_duplicate_parameters,
FunctionLiteral::FunctionType function_type,
FunctionLiteral::EagerCompileHint eager_compile_hint, int position,
bool has_braces, int function_literal_id) {
bool has_braces, int function_literal_id,
ProducedPreParsedScopeData* produced_preparsed_scope_data = nullptr) {
DCHECK_NULL(produced_preparsed_scope_data);
return PreParserExpression::Default();
}
@ -904,14 +908,14 @@ class PreParser : public ParserBase<PreParser> {
AstValueFactory* ast_value_factory,
PendingCompilationErrorHandler* pending_error_handler,
RuntimeCallStats* runtime_call_stats,
PreParsedScopeData* preparsed_scope_data = nullptr,
bool parsing_on_main_thread = true)
: ParserBase<PreParser>(zone, scanner, stack_limit, nullptr,
ast_value_factory, runtime_call_stats,
preparsed_scope_data, parsing_on_main_thread),
parsing_on_main_thread),
use_counts_(nullptr),
track_unresolved_variables_(false),
pending_error_handler_(pending_error_handler) {}
pending_error_handler_(pending_error_handler),
produced_preparsed_scope_data_(nullptr) {}
static bool IsPreParser() { return true; }
@ -931,13 +935,21 @@ class PreParser : public ParserBase<PreParser> {
// keyword and parameters, and have consumed the initial '{'.
// At return, unless an error occurred, the scanner is positioned before the
// the final '}'.
PreParseResult PreParseFunction(const AstRawString* function_name,
FunctionKind kind,
FunctionLiteral::FunctionType function_type,
DeclarationScope* function_scope,
bool parsing_module,
bool track_unresolved_variables,
bool may_abort, int* use_counts);
PreParseResult PreParseFunction(
const AstRawString* function_name, FunctionKind kind,
FunctionLiteral::FunctionType function_type,
DeclarationScope* function_scope, bool parsing_module,
bool track_unresolved_variables, bool may_abort, int* use_counts,
ProducedPreParsedScopeData** produced_preparser_scope_data);
ProducedPreParsedScopeData* produced_preparsed_scope_data() const {
return produced_preparsed_scope_data_;
}
void set_produced_preparsed_scope_data(
ProducedPreParsedScopeData* produced_preparsed_scope_data) {
produced_preparsed_scope_data_ = produced_preparsed_scope_data;
}
private:
// These types form an algebra over syntactic categories that is just
@ -959,6 +971,7 @@ class PreParser : public ParserBase<PreParser> {
SkipFunction(const AstRawString* name, FunctionKind kind,
FunctionLiteral::FunctionType function_type,
DeclarationScope* function_scope, int* num_parameters,
ProducedPreParsedScopeData** produced_preparsed_scope_data,
bool is_inner_function, bool may_abort, bool* ok) {
UNREACHABLE();
}
@ -1735,6 +1748,8 @@ class PreParser : public ParserBase<PreParser> {
bool track_unresolved_variables_;
PreParserLogger log_;
PendingCompilationErrorHandler* pending_error_handler_;
ProducedPreParsedScopeData* produced_preparsed_scope_data_;
};
PreParserExpression PreParser::SpreadCall(PreParserExpression function,

View File

@ -7,6 +7,7 @@
#include "src/objects-inl.h"
#include "src/parsing/parse-info.h"
#include "src/parsing/parsing.h"
#include "src/parsing/preparsed-scope-data.h"
#include "test/cctest/cctest.h"
#include "test/cctest/scope-test-helper.h"
@ -35,27 +36,6 @@ TEST(PreParserScopeAnalysis) {
i::HandleScope scope(isolate);
LocalContext env;
/* Test the following cases:
1)
(function outer() {
function test() { ... }
})();
(Laziness boundary at "test".)
2)
(function outer() {
function inner() { function test() { ... } }
})();
(Laziness boundary at "test".)
3)
(function outer() {
function inner() { () => { ... } }
})();
Inner arrow functions are never lazy, so the corresponding case is missing.
*/
struct {
const char* code;
bool strict_outer;
@ -64,35 +44,23 @@ TEST(PreParserScopeAnalysis) {
std::vector<unsigned> location; // "Directions" to the relevant scope.
} outers[] = {
// Normal case (test function at the laziness boundary):
{"(function outer() { function test(%s) { %s } })();",
{"(function outer() { function test(%s) { %s \n"
"function skippable() { } } })();",
false,
false,
false,
{0, 0}},
{"(function outer() { let test2 = function test(%s) { %s } })();",
false,
false,
false,
{0, 0}},
// Test function deeper:
{"(function outer() { function inner() { "
"function test(%s) { %s } } })();",
false,
false,
false,
{0, 0}},
{"(function outer() { function inner() { "
"let test2 = function test(%s) { %s } } })();",
{"(function outer() { let test2 = function test(%s) { %s \n"
"function skippable() { } } })();",
false,
false,
false,
{0, 0}},
// Arrow functions (they can never be at the laziness boundary):
{"(function outer() { function inner() { (%s) => { %s } } })();",
{"(function outer() { function inner() { (%s) => { %s } \n"
"function skippable() { } } })();",
false,
false,
true,
@ -100,57 +68,50 @@ TEST(PreParserScopeAnalysis) {
// Repeat the above mentioned cases w/ outer function declaring itself
// strict:
{"(function outer() { 'use strict'; function test(%s) { %s } })();",
{"(function outer() { 'use strict'; function test(%s) { %s \n"
"function skippable() { } } })();",
true,
false,
false,
{0, 0}},
{"(function outer() { 'use strict'; function inner() { "
"function test(%s) { %s } } })();",
true,
false,
false,
{0, 0}},
{"(function outer() { 'use strict'; function inner() { "
"(%s) => { %s } } })();",
"(%s) => { %s } \nfunction skippable() { } } })();",
true,
false,
true,
{0, 0}},
// ... and with the test function declaring itself strict:
{"(function outer() { function test(%s) { 'use strict'; %s } })();",
{"(function outer() { function test(%s) { 'use strict'; %s \n"
"function skippable() { } } })();",
false,
true,
false,
{0, 0}},
{"(function outer() { function inner() { "
"function test(%s) { 'use strict'; %s } } })();",
false,
true,
false,
{0, 0}},
{"(function outer() { function inner() { "
"(%s) => { 'use strict'; %s } } })();",
"(%s) => { 'use strict'; %s } \nfunction skippable() { } } })();",
false,
true,
true,
{0, 0}},
// Methods containing skippable functions. Cannot test at the laziness
// boundary, since there's no way to force eager parsing of a method.
{"class MyClass { constructor() { function test(%s) { %s } } }",
// Methods containing skippable functions.
{"class MyClass { constructor(%s) { %s \n"
"function skippable() { } } }",
true,
true,
false,
{0, 0, 0}},
{0, 0}},
{"class MyClass { mymethod() { function test(%s) { %s } } }",
{"class MyClass { test(%s) { %s \n"
"function skippable() { } } }",
true,
true,
false,
// The default constructor is scope 0 inside the class.
{0, 1, 0}},
{0, 1}},
// FIXME(marja): Generators and async functions
};
@ -691,6 +652,18 @@ TEST(PreParserScopeAnalysis) {
// parsing.
CHECK(i::parsing::ParseProgram(&lazy_info, isolate));
// Retrieve the scope data we produced.
i::Scope* scope_with_data = i::ScopeTestHelper::FindScope(
lazy_info.literal()->scope(), outers[outer_ix].location);
i::ProducedPreParsedScopeData* produced_data =
scope_with_data->AsDeclarationScope()
->produced_preparsed_scope_data();
i::MaybeHandle<i::PreParsedScopeData> maybe_produced_data_on_heap =
produced_data->Serialize(isolate);
DCHECK(!maybe_produced_data_on_heap.is_null());
i::Handle<i::PreParsedScopeData> produced_data_on_heap =
maybe_produced_data_on_heap.ToHandleChecked();
// Then parse eagerly and check against the scope data.
script = factory->NewScript(source);
@ -720,7 +693,15 @@ TEST(PreParserScopeAnalysis) {
CHECK_NULL(unallocated_scope->sibling());
CHECK(unallocated_scope->is_function_scope());
lazy_info.preparsed_scope_data()->RestoreData(
// Mark all inner functions as "skipped", so that we don't try to restore
// data for them. No test should contain eager functions, because we
// cannot properly decide whether we have or don't have data for them.
i::ScopeTestHelper::MarkInnerFunctionsAsSkipped(unallocated_scope);
i::ConsumedPreParsedScopeData* consumed_preparsed_scope_data =
lazy_info.consumed_preparsed_scope_data();
consumed_preparsed_scope_data->SetData(produced_data_on_heap);
consumed_preparsed_scope_data->SkipFunctionDataForTesting();
consumed_preparsed_scope_data->RestoreScopeAllocationData(
unallocated_scope->AsDeclarationScope());
i::ScopeTestHelper::AllocateWithoutVariableResolution(unallocated_scope);

View File

@ -28,7 +28,12 @@ class ScopeTestHelper {
baseline->AsDeclarationScope()->function_kind() ==
scope->AsDeclarationScope()->function_kind());
if (!PreParsedScopeData::ScopeNeedsData(baseline)) {
if (!ProducedPreParsedScopeData::ScopeNeedsData(baseline)) {
return;
}
if (scope->is_declaration_scope() &&
scope->AsDeclarationScope()->is_skipped_function()) {
return;
}
@ -93,6 +98,17 @@ class ScopeTestHelper {
}
return scope;
}
static void MarkInnerFunctionsAsSkipped(Scope* scope) {
for (Scope* inner = scope->inner_scope(); inner != nullptr;
inner = inner->sibling()) {
if (inner->scope_type() == ScopeType::FUNCTION_SCOPE &&
!inner->AsDeclarationScope()->is_arrow_scope()) {
inner->AsDeclarationScope()->set_is_skipped_function(true);
}
MarkInnerFunctionsAsSkipped(inner);
}
}
};
} // namespace internal
} // namespace v8

View File

@ -136,3 +136,70 @@ TestSkippedFunctionInsideLoopInitializer();
lazy(9)(8, 7);
assertEquals(34, result);
})();
function TestSkippingDeeperLazyFunctions() {
let result = 0;
function inner_lazy(ctxt_alloc_param) {
let ctxt_alloc_var = 13;
function skip_me() {
result = ctxt_alloc_param + ctxt_alloc_var;
}
return skip_me;
}
let f = inner_lazy(12);
f();
assertEquals(25, result);
}
TestSkippingDeeperLazyFunctions();
function TestEagerFunctionsBetweenLazyFunctions() {
let result = 0;
// We produce one data set for TestEagerFunctionsBetweenLazyFunctions and
// another one for inner. The variable data for eager belongs to the former
// data set.
let ctxt_allocated1 = 3;
(function eager() {
let ctxt_allocated2 = 4;
function inner() {
result = ctxt_allocated1 + ctxt_allocated2;
}
return inner;
})()();
assertEquals(7, result);
}
TestEagerFunctionsBetweenLazyFunctions();
function TestEagerNotIifeFunctionsBetweenLazyFunctions() {
let result = 0;
// We produce one data set for TestEagerFunctionsBetweenLazyFunctions and
// another one for inner. The variable data for eager belongs to the former
// data set.
let ctxt_allocated1 = 3;
(function eager_not_iife() {
let ctxt_allocated2 = 4;
function inner() {
result = ctxt_allocated1 + ctxt_allocated2;
}
return inner;
}); // Function not called; not an iife.
// This is just a regression test. We cannot test that the context allocation
// was done correctly (since there's no way to call eager_not_iife), but code
// like this used to trigger some DCHECKs.
}
TestEagerNotIifeFunctionsBetweenLazyFunctions();
// Regression test for functions inside a lazy arrow function. (Only top-level
// arrow functions are lazy, so this cannot be wrapped in a function.)
result = 0;
let f1 = (ctxt_alloc_param) => {
let ctxt_alloc_var = 10;
function inner() {
result = ctxt_alloc_param + ctxt_alloc_var;
}
return inner;
}
f1(9)();
assertEquals(19, result);

View File

@ -71,18 +71,18 @@ INSTANCE_TYPES = {
167: "MODULE_TYPE",
168: "MODULE_INFO_ENTRY_TYPE",
169: "ASYNC_GENERATOR_REQUEST_TYPE",
170: "FIXED_ARRAY_TYPE",
171: "TRANSITION_ARRAY_TYPE",
172: "SHARED_FUNCTION_INFO_TYPE",
173: "CELL_TYPE",
174: "WEAK_CELL_TYPE",
175: "PROPERTY_CELL_TYPE",
176: "SMALL_ORDERED_HASH_MAP_TYPE",
177: "SMALL_ORDERED_HASH_SET_TYPE",
178: "PADDING_TYPE_1",
179: "PADDING_TYPE_2",
180: "PADDING_TYPE_3",
181: "PADDING_TYPE_4",
170: "PREPARSED_SCOPE_DATA_TYPE",
171: "FIXED_ARRAY_TYPE",
172: "TRANSITION_ARRAY_TYPE",
173: "SHARED_FUNCTION_INFO_TYPE",
174: "CELL_TYPE",
175: "WEAK_CELL_TYPE",
176: "PROPERTY_CELL_TYPE",
177: "SMALL_ORDERED_HASH_MAP_TYPE",
178: "SMALL_ORDERED_HASH_SET_TYPE",
179: "PADDING_TYPE_1",
180: "PADDING_TYPE_2",
181: "PADDING_TYPE_3",
182: "JS_PROXY_TYPE",
183: "JS_GLOBAL_OBJECT_TYPE",
184: "JS_GLOBAL_PROXY_TYPE",
@ -157,7 +157,7 @@ KNOWN_MAPS = {
0x02201: (137, "FreeSpaceMap"),
0x02259: (131, "MetaMap"),
0x022b1: (130, "NullMap"),
0x02309: (170, "FixedArrayMap"),
0x02309: (171, "FixedArrayMap"),
0x02361: (8, "OneByteInternalizedStringMap"),
0x023b9: (148, "OnePointerFillerMap"),
0x02411: (148, "TwoPointerFillerMap"),
@ -167,43 +167,43 @@ KNOWN_MAPS = {
0x02571: (130, "TheHoleMap"),
0x025c9: (130, "BooleanMap"),
0x02621: (135, "ByteArrayMap"),
0x02679: (170, "FixedCOWArrayMap"),
0x026d1: (170, "HashTableMap"),
0x02679: (171, "FixedCOWArrayMap"),
0x026d1: (171, "HashTableMap"),
0x02729: (128, "SymbolMap"),
0x02781: (72, "OneByteStringMap"),
0x027d9: (170, "ScopeInfoMap"),
0x02831: (172, "SharedFunctionInfoMap"),
0x027d9: (171, "ScopeInfoMap"),
0x02831: (173, "SharedFunctionInfoMap"),
0x02889: (132, "CodeMap"),
0x028e1: (170, "FunctionContextMap"),
0x02939: (173, "CellMap"),
0x02991: (174, "WeakCellMap"),
0x029e9: (175, "GlobalPropertyCellMap"),
0x028e1: (171, "FunctionContextMap"),
0x02939: (174, "CellMap"),
0x02991: (175, "WeakCellMap"),
0x029e9: (176, "GlobalPropertyCellMap"),
0x02a41: (134, "ForeignMap"),
0x02a99: (171, "TransitionArrayMap"),
0x02a99: (172, "TransitionArrayMap"),
0x02af1: (130, "ArgumentsMarkerMap"),
0x02b49: (130, "ExceptionMap"),
0x02ba1: (130, "TerminationExceptionMap"),
0x02bf9: (130, "OptimizedOutMap"),
0x02c51: (130, "StaleRegisterMap"),
0x02ca9: (170, "NativeContextMap"),
0x02d01: (170, "ModuleContextMap"),
0x02d59: (170, "EvalContextMap"),
0x02db1: (170, "ScriptContextMap"),
0x02e09: (170, "BlockContextMap"),
0x02e61: (170, "CatchContextMap"),
0x02eb9: (170, "WithContextMap"),
0x02ca9: (171, "NativeContextMap"),
0x02d01: (171, "ModuleContextMap"),
0x02d59: (171, "EvalContextMap"),
0x02db1: (171, "ScriptContextMap"),
0x02e09: (171, "BlockContextMap"),
0x02e61: (171, "CatchContextMap"),
0x02eb9: (171, "WithContextMap"),
0x02f11: (147, "FixedDoubleArrayMap"),
0x02f69: (133, "MutableHeapNumberMap"),
0x02fc1: (170, "OrderedHashTableMap"),
0x03019: (170, "SloppyArgumentsElementsMap"),
0x03071: (176, "SmallOrderedHashMapMap"),
0x030c9: (177, "SmallOrderedHashSetMap"),
0x02fc1: (171, "OrderedHashTableMap"),
0x03019: (171, "SloppyArgumentsElementsMap"),
0x03071: (177, "SmallOrderedHashMapMap"),
0x030c9: (178, "SmallOrderedHashSetMap"),
0x03121: (187, "JSMessageObjectMap"),
0x03179: (136, "BytecodeArrayMap"),
0x031d1: (170, "ModuleInfoMap"),
0x03229: (173, "NoClosuresCellMap"),
0x03281: (173, "OneClosureCellMap"),
0x032d9: (173, "ManyClosuresCellMap"),
0x031d1: (171, "ModuleInfoMap"),
0x03229: (174, "NoClosuresCellMap"),
0x03281: (174, "OneClosureCellMap"),
0x032d9: (174, "ManyClosuresCellMap"),
0x03331: (64, "StringMap"),
0x03389: (73, "ConsOneByteStringMap"),
0x033e1: (65, "ConsStringMap"),
@ -234,10 +234,10 @@ KNOWN_MAPS = {
0x03c79: (145, "FixedFloat64ArrayMap"),
0x03cd1: (146, "FixedUint8ClampedArrayMap"),
0x03d29: (157, "ScriptMap"),
0x03d81: (170, "FeedbackVectorMap"),
0x03dd9: (170, "DebugEvaluateContextMap"),
0x03e31: (170, "ScriptContextTableMap"),
0x03e89: (170, "UnseededNumberDictionaryMap"),
0x03d81: (171, "FeedbackVectorMap"),
0x03dd9: (171, "DebugEvaluateContextMap"),
0x03e31: (171, "ScriptContextTableMap"),
0x03e89: (171, "UnseededNumberDictionaryMap"),
0x03ee1: (190, "ExternalMap"),
0x03f39: (106, "NativeSourceStringMap"),
0x03f91: (152, "InterceptorInfoMap"),
@ -261,6 +261,7 @@ KNOWN_MAPS = {
0x045c1: (167, "ModuleMap"),
0x04619: (168, "ModuleInfoEntryMap"),
0x04671: (169, "AsyncGeneratorRequestMap"),
0x046c9: (170, "PreParsedScopeDataMap"),
}
# List of known V8 objects.
@ -293,21 +294,21 @@ KNOWN_OBJECTS = {
("OLD_SPACE", 0x02749): "EmptyFixedFloat64Array",
("OLD_SPACE", 0x02769): "EmptyFixedUint8ClampedArray",
("OLD_SPACE", 0x02789): "EmptyScript",
("OLD_SPACE", 0x02811): "UndefinedCell",
("OLD_SPACE", 0x02821): "EmptySloppyArgumentsElements",
("OLD_SPACE", 0x02841): "EmptySlowElementDictionary",
("OLD_SPACE", 0x02889): "EmptyPropertyCell",
("OLD_SPACE", 0x028b1): "EmptyWeakCell",
("OLD_SPACE", 0x028c9): "ArrayProtector",
("OLD_SPACE", 0x028f1): "IsConcatSpreadableProtector",
("OLD_SPACE", 0x02901): "SpeciesProtector",
("OLD_SPACE", 0x02929): "StringLengthProtector",
("OLD_SPACE", 0x02951): "FastArrayIterationProtector",
("OLD_SPACE", 0x02961): "ArrayIteratorProtector",
("OLD_SPACE", 0x02989): "ArrayBufferNeuteringProtector",
("OLD_SPACE", 0x029b1): "InfinityValue",
("OLD_SPACE", 0x029c1): "MinusZeroValue",
("OLD_SPACE", 0x029d1): "MinusInfinityValue",
("OLD_SPACE", 0x02809): "UndefinedCell",
("OLD_SPACE", 0x02819): "EmptySloppyArgumentsElements",
("OLD_SPACE", 0x02839): "EmptySlowElementDictionary",
("OLD_SPACE", 0x02881): "EmptyPropertyCell",
("OLD_SPACE", 0x028a9): "EmptyWeakCell",
("OLD_SPACE", 0x028c1): "ArrayProtector",
("OLD_SPACE", 0x028e9): "IsConcatSpreadableProtector",
("OLD_SPACE", 0x028f9): "SpeciesProtector",
("OLD_SPACE", 0x02921): "StringLengthProtector",
("OLD_SPACE", 0x02949): "FastArrayIterationProtector",
("OLD_SPACE", 0x02959): "ArrayIteratorProtector",
("OLD_SPACE", 0x02981): "ArrayBufferNeuteringProtector",
("OLD_SPACE", 0x029a9): "InfinityValue",
("OLD_SPACE", 0x029b9): "MinusZeroValue",
("OLD_SPACE", 0x029c9): "MinusInfinityValue",
}
# List of known V8 Frame Markers.