[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:
parent
27b0d6a9fc
commit
937b5011b8
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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) \
|
||||
|
@ -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_;
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user