Index: src/parser.cc |
diff --git a/src/parser.cc b/src/parser.cc |
index a626d99fa0a078cbaffb97deb3fb448e4d6c8dd2..37e903aac9c1e9a249718f61351800e3edc9464f 100644 |
--- a/src/parser.cc |
+++ b/src/parser.cc |
@@ -832,10 +832,145 @@ void Parser::ReportMessageAt(Scanner::Location source_location, |
} |
+// Base class containing common code for the different finder classes used by |
+// the parser. |
+class ParserFinder { |
+ protected: |
+ ParserFinder() {} |
+ static Assignment* AsAssignment(Statement* stat) { |
+ if (stat == NULL) return NULL; |
+ ExpressionStatement* exp_stat = stat->AsExpressionStatement(); |
+ if (exp_stat == NULL) return NULL; |
+ return exp_stat->expression()->AsAssignment(); |
+ } |
+}; |
+ |
+ |
+// An InitializationBlockFinder finds and marks sequences of statements of the |
+// form expr.a = ...; expr.b = ...; etc. |
+class InitializationBlockFinder : public ParserFinder { |
+ public: |
+ // We find and mark the initialization blocks in top level |
+ // non-looping code only. This is because the optimization prevents |
+ // reuse of the map transitions, so it should be used only for code |
+ // that will only be run once. |
+ InitializationBlockFinder(Scope* top_scope, Target* target) |
+ : enabled_(top_scope->DeclarationScope()->is_global_scope() && |
+ !IsLoopTarget(target)), |
+ first_in_block_(NULL), |
+ last_in_block_(NULL), |
+ block_size_(0) {} |
+ |
+ ~InitializationBlockFinder() { |
+ if (!enabled_) return; |
+ if (InBlock()) EndBlock(); |
+ } |
+ |
+ void Update(Statement* stat) { |
+ if (!enabled_) return; |
+ Assignment* assignment = AsAssignment(stat); |
+ if (InBlock()) { |
+ if (BlockContinues(assignment)) { |
+ UpdateBlock(assignment); |
+ } else { |
+ EndBlock(); |
+ } |
+ } |
+ if (!InBlock() && (assignment != NULL) && |
+ (assignment->op() == Token::ASSIGN)) { |
+ StartBlock(assignment); |
+ } |
+ } |
+ |
+ private: |
+ // The minimum number of contiguous assignment that will |
+ // be treated as an initialization block. Benchmarks show that |
+ // the overhead exceeds the savings below this limit. |
+ static const int kMinInitializationBlock = 3; |
+ |
+ static bool IsLoopTarget(Target* target) { |
+ while (target != NULL) { |
+ if (target->node()->AsIterationStatement() != NULL) return true; |
+ target = target->previous(); |
+ } |
+ return false; |
+ } |
+ |
+ // Returns true if the expressions appear to denote the same object. |
+ // In the context of initialization blocks, we only consider expressions |
+ // of the form 'expr.x' or expr["x"]. |
+ static bool SameObject(Expression* e1, Expression* e2) { |
+ VariableProxy* v1 = e1->AsVariableProxy(); |
+ VariableProxy* v2 = e2->AsVariableProxy(); |
+ if (v1 != NULL && v2 != NULL) { |
+ return v1->name()->Equals(*v2->name()); |
+ } |
+ Property* p1 = e1->AsProperty(); |
+ Property* p2 = e2->AsProperty(); |
+ if ((p1 == NULL) || (p2 == NULL)) return false; |
+ Literal* key1 = p1->key()->AsLiteral(); |
+ Literal* key2 = p2->key()->AsLiteral(); |
+ if ((key1 == NULL) || (key2 == NULL)) return false; |
+ if (!key1->handle()->IsString() || !key2->handle()->IsString()) { |
+ return false; |
+ } |
+ String* name1 = String::cast(*key1->handle()); |
+ String* name2 = String::cast(*key2->handle()); |
+ if (!name1->Equals(name2)) return false; |
+ return SameObject(p1->obj(), p2->obj()); |
+ } |
+ |
+ // Returns true if the expressions appear to denote different properties |
+ // of the same object. |
+ static bool PropertyOfSameObject(Expression* e1, Expression* e2) { |
+ Property* p1 = e1->AsProperty(); |
+ Property* p2 = e2->AsProperty(); |
+ if ((p1 == NULL) || (p2 == NULL)) return false; |
+ return SameObject(p1->obj(), p2->obj()); |
+ } |
+ |
+ bool BlockContinues(Assignment* assignment) { |
+ if ((assignment == NULL) || (first_in_block_ == NULL)) return false; |
+ if (assignment->op() != Token::ASSIGN) return false; |
+ return PropertyOfSameObject(first_in_block_->target(), |
+ assignment->target()); |
+ } |
+ |
+ void StartBlock(Assignment* assignment) { |
+ first_in_block_ = assignment; |
+ last_in_block_ = assignment; |
+ block_size_ = 1; |
+ } |
+ |
+ void UpdateBlock(Assignment* assignment) { |
+ last_in_block_ = assignment; |
+ ++block_size_; |
+ } |
+ |
+ void EndBlock() { |
+ if (block_size_ >= kMinInitializationBlock) { |
+ first_in_block_->mark_block_start(); |
+ last_in_block_->mark_block_end(); |
+ } |
+ last_in_block_ = first_in_block_ = NULL; |
+ block_size_ = 0; |
+ } |
+ |
+ bool InBlock() { return first_in_block_ != NULL; } |
+ |
+ const bool enabled_; |
+ Assignment* first_in_block_; |
+ Assignment* last_in_block_; |
+ int block_size_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(InitializationBlockFinder); |
+}; |
+ |
+ |
// A ThisNamedPropertyAssignmentFinder finds and marks statements of the form |
// this.x = ...;, where x is a named property. It also determines whether a |
// function contains only assignments of this type. |
-class ThisNamedPropertyAssignmentFinder { |
+class ThisNamedPropertyAssignmentFinder : public ParserFinder { |
public: |
ThisNamedPropertyAssignmentFinder(Isolate* isolate, Zone* zone) |
: isolate_(isolate), |
@@ -846,13 +981,6 @@ class ThisNamedPropertyAssignmentFinder { |
zone_(zone) { |
} |
- static Assignment* AsAssignment(Statement* stat) { |
- if (stat == NULL) return NULL; |
- ExpressionStatement* exp_stat = stat->AsExpressionStatement(); |
- if (exp_stat == NULL) return NULL; |
- return exp_stat->expression()->AsAssignment(); |
- } |
- |
void Update(Scope* scope, Statement* stat) { |
// Bail out if function already has property assignment that are |
// not simple this property assignments. |
@@ -1018,6 +1146,7 @@ void* Parser::ParseSourceElements(ZoneList<Statement*>* processor, |
TargetScope scope(&this->target_stack_); |
ASSERT(processor != NULL); |
+ InitializationBlockFinder block_finder(top_scope_, target_stack_); |
ThisNamedPropertyAssignmentFinder this_property_assignment_finder(isolate(), |
zone()); |
bool directive_prologue = true; // Parsing directive prologue. |
@@ -1072,6 +1201,7 @@ void* Parser::ParseSourceElements(ZoneList<Statement*>* processor, |
} |
} |
+ block_finder.Update(stat); |
// Find and mark all assignments to named properties in this (this.x =) |
if (top_scope_->is_function_scope()) { |
this_property_assignment_finder.Update(top_scope_, stat); |
@@ -1224,11 +1354,13 @@ Module* Parser::ParseModuleLiteral(bool* ok) { |
TargetCollector collector(zone()); |
Target target(&this->target_stack_, &collector); |
Target target_body(&this->target_stack_, body); |
+ InitializationBlockFinder block_finder(top_scope_, target_stack_); |
while (peek() != Token::RBRACE) { |
Statement* stat = ParseModuleElement(NULL, CHECK_OK); |
if (stat && !stat->IsEmpty()) { |
body->AddStatement(stat, zone()); |
+ block_finder.Update(stat); |
} |
} |
} |
@@ -1904,10 +2036,12 @@ Block* Parser::ParseBlock(ZoneStringList* labels, bool* ok) { |
Block* result = factory()->NewBlock(labels, 16, false); |
Target target(&this->target_stack_, result); |
Expect(Token::LBRACE, CHECK_OK); |
+ InitializationBlockFinder block_finder(top_scope_, target_stack_); |
while (peek() != Token::RBRACE) { |
Statement* stat = ParseStatement(NULL, CHECK_OK); |
if (stat && !stat->IsEmpty()) { |
result->AddStatement(stat, zone()); |
+ block_finder.Update(stat); |
} |
} |
Expect(Token::RBRACE, CHECK_OK); |
@@ -1932,11 +2066,13 @@ Block* Parser::ParseScopedBlock(ZoneStringList* labels, bool* ok) { |
TargetCollector collector(zone()); |
Target target(&this->target_stack_, &collector); |
Target target_body(&this->target_stack_, body); |
+ InitializationBlockFinder block_finder(top_scope_, target_stack_); |
while (peek() != Token::RBRACE) { |
Statement* stat = ParseBlockElement(NULL, CHECK_OK); |
if (stat && !stat->IsEmpty()) { |
body->AddStatement(stat, zone()); |
+ block_finder.Update(stat); |
} |
} |
} |