// Inference is performed in cases when an anonymous function is assigned
// to a variable or a property (see test-func-name-inference.cc for examples.)
//
-// The basic idea is that during AST traversal LHSs of expressions are
-// always visited before RHSs. Thus, during visiting the LHS, a name can be
-// collected, and during visiting the RHS, a function literal can be collected.
-// Inference is performed while leaving the assignment node.
-class FuncNameInferrer BASE_EMBEDDED {
+// The basic idea is that during parsing of LHSs of certain expressions
+// (assignments, declarations, object literals) we collect name strings,
+// and during parsing of the RHS, a function literal can be collected. After
+// parsing the RHS we can infer a name for function literals that do not have
+// a name.
+class FuncNameInferrer : public ZoneObject {
public:
FuncNameInferrer()
: entries_stack_(10),
}
// Pushes an encountered name onto names stack when in collection state.
- void PushName(Handle<String> name) {
- if (IsOpen()) {
- names_stack_.Add(name);
- }
- }
+ void PushLiteralName(Handle<String> name);
+
+ void PushVariableName(Handle<String> name);
// Adds a function to infer name for.
void AddFunction(FunctionLiteral* func_to_infer) {
}
// Infers a function name and leaves names collection state.
- void InferAndLeave() {
+ void Infer() {
ASSERT(IsOpen());
if (!funcs_to_infer_.is_empty()) {
InferFunctionsNames();
}
+ }
+
+ // Infers a function name and leaves names collection state.
+ void Leave() {
+ ASSERT(IsOpen());
names_stack_.Rewind(entries_stack_.RemoveLast());
}
};
-// A wrapper class that automatically calls InferAndLeave when
-// leaving scope.
-class ScopedFuncNameInferrer BASE_EMBEDDED {
- public:
- explicit ScopedFuncNameInferrer(FuncNameInferrer* inferrer)
- : inferrer_(inferrer),
- is_entered_(false) {}
-
- ~ScopedFuncNameInferrer() {
- if (is_entered_) {
- inferrer_->InferAndLeave();
- }
- }
-
- // Triggers the wrapped inferrer into name collection state.
- void Enter() {
- inferrer_->Enter();
- is_entered_ = true;
- }
-
- private:
- FuncNameInferrer* inferrer_;
- bool is_entered_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedFuncNameInferrer);
-};
-
-
} } // namespace v8::internal
#endif // V8_FUNC_NAME_INFERRER_H_
#include "bootstrapper.h"
#include "codegen.h"
#include "compiler.h"
+#include "func-name-inferrer.h"
#include "messages.h"
#include "parser.h"
#include "platform.h"
bool is_pre_parsing_;
ScriptDataImpl* pre_data_;
bool seen_loop_stmt_; // Used for inner loop detection.
+ FuncNameInferrer* fni_;
bool inside_with() const { return with_nesting_level_ > 0; }
ParserFactory* factory() const { return factory_; }
log_(log),
is_pre_parsing_(is_pre_parsing == PREPARSE),
pre_data_(pre_data),
- seen_loop_stmt_(false) {
+ seen_loop_stmt_(false),
+ fni_(NULL) {
}
HistogramTimerScope timer(&Counters::parse);
Counters::total_parse_size.Increment(source->length());
+ fni_ = new FuncNameInferrer();
// Initialize parser state.
source->TryFlatten();
HistogramTimerScope timer(&Counters::parse_lazy);
Counters::total_parse_size.Increment(source->length());
+ fni_ = new FuncNameInferrer();
+ fni_->PushEnclosingName(name);
+
// Initialize parser state.
source->TryFlatten();
scanner_.Initialize(source, start_position, end_position, JAVASCRIPT);
VariableProxy* last_var = NULL; // the last variable declared
int nvars = 0; // the number of variables declared
do {
+ if (fni_ != NULL) fni_->Enter();
+
// Parse variable name.
if (nvars > 0) Consume(Token::COMMA);
Handle<String> name = ParseIdentifier(CHECK_OK);
+ if (fni_ != NULL) fni_->PushVariableName(name);
// Declare variable.
// Note that we *always* must treat the initial value via a separate init
Expect(Token::ASSIGN, CHECK_OK);
position = scanner().location().beg_pos;
value = ParseAssignmentExpression(accept_IN, CHECK_OK);
+ // Don't infer if it is "a = function(){...}();"-like expression.
+ if (fni_ != NULL && value->AsCall() == NULL) fni_->Infer();
}
// Make sure that 'const c' actually initializes 'c' to undefined
Assignment* assignment = NEW(Assignment(op, last_var, value, position));
if (block) block->AddStatement(NEW(ExpressionStatement(assignment)));
}
+
+ if (fni_ != NULL) fni_->Leave();
} while (peek() == Token::COMMA);
if (!is_const && nvars == 1) {
// ConditionalExpression
// LeftHandSideExpression AssignmentOperator AssignmentExpression
+ if (fni_ != NULL) fni_->Enter();
Expression* expression = ParseConditionalExpression(accept_IN, CHECK_OK);
if (!Token::IsAssignmentOp(peek())) {
+ if (fni_ != NULL) fni_->Leave();
// Parsed conditional expression only (no assignment).
return expression;
}
temp_scope_->AddProperty();
}
+ if (fni_ != NULL) {
+ // Check if the right hand side is a call to avoid inferring a
+ // name if we're dealing with "a = function(){...}();"-like
+ // expression.
+ if ((op == Token::INIT_VAR
+ || op == Token::INIT_CONST
+ || op == Token::ASSIGN)
+ && (right->AsCall() == NULL)) {
+ fni_->Infer();
+ }
+ fni_->Leave();
+ }
+
return NEW(Assignment(op, expression, right, pos));
}
int pos = scanner().location().beg_pos;
Handle<String> name = ParseIdentifierName(CHECK_OK);
result = factory()->NewProperty(result, NEW(Literal(name)), pos);
+ if (fni_ != NULL) fni_->PushLiteralName(name);
break;
}
int pos = scanner().location().beg_pos;
Handle<String> name = ParseIdentifierName(CHECK_OK);
result = factory()->NewProperty(result, NEW(Literal(name)), pos);
+ if (fni_ != NULL) fni_->PushLiteralName(name);
break;
}
case Token::LPAREN: {
case Token::IDENTIFIER: {
Handle<String> name = ParseIdentifier(CHECK_OK);
+ if (fni_ != NULL) fni_->PushVariableName(name);
if (is_pre_parsing_) {
result = VariableProxySentinel::identifier_proxy();
} else {
factory()->LookupSymbol(scanner_.literal_string(),
scanner_.literal_length());
result = NEW(Literal(symbol));
+ if (fni_ != NULL) fni_->PushLiteralName(symbol);
break;
}
Expect(Token::LBRACE, CHECK_OK);
while (peek() != Token::RBRACE) {
+ if (fni_ != NULL) fni_->Enter();
+
Literal* key = NULL;
Token::Value next = peek();
switch (next) {
bool is_setter = false;
Handle<String> id =
ParseIdentifierOrGetOrSet(&is_getter, &is_setter, CHECK_OK);
+ if (fni_ != NULL) fni_->PushLiteralName(id);
+
if ((is_getter || is_setter) && peek() != Token::COLON) {
ObjectLiteral::Property* property =
ParseObjectLiteralGetSet(is_getter, CHECK_OK);
}
properties.Add(property);
if (peek() != Token::RBRACE) Expect(Token::COMMA, CHECK_OK);
+
+ if (fni_ != NULL) {
+ fni_->Infer();
+ fni_->Leave();
+ }
continue; // restart the while
}
// Failed to parse as get/set property, so it's just a property
Handle<String> string =
factory()->LookupSymbol(scanner_.literal_string(),
scanner_.literal_length());
+ if (fni_ != NULL) fni_->PushLiteralName(string);
uint32_t index;
if (!string.is_null() && string->AsArrayIndex(&index)) {
key = NewNumberLiteral(index);
// TODO(1240767): Consider allowing trailing comma.
if (peek() != Token::RBRACE) Expect(Token::COMMA, CHECK_OK);
+
+ if (fni_ != NULL) {
+ fni_->Infer();
+ fni_->Leave();
+ }
}
Expect(Token::RBRACE, CHECK_OK);
// Computation of literal_index must happen before pre parse bailout.
// when peeling or unrolling such a loop.
seen_loop_stmt_ = true;
+ if (fni_ != NULL && !is_named) fni_->AddFunction(function_literal);
return function_literal;
}
}
#include "v8.h"
#include "ast.h"
-#include "func-name-inferrer.h"
#include "scopes.h"
#include "rewriter.h"
class AstOptimizer: public AstVisitor {
public:
explicit AstOptimizer() : has_function_literal_(false) {}
- explicit AstOptimizer(Handle<String> enclosing_name)
- : has_function_literal_(false) {
- func_name_inferrer_.PushEnclosingName(enclosing_name);
- }
void Optimize(ZoneList<Statement*>* statements);
// Used for loop condition analysis. Cleared before visiting a loop
// condition, set when a function literal is visited.
bool has_function_literal_;
- // Helper object for function name inferring.
- FuncNameInferrer func_name_inferrer_;
// Helpers
void OptimizeArguments(ZoneList<Expression*>* arguments);
void AstOptimizer::VisitFunctionLiteral(FunctionLiteral* node) {
has_function_literal_ = true;
-
- if (node->name()->length() == 0) {
- // Anonymous function.
- func_name_inferrer_.AddFunction(node);
- }
}
var->type()->SetAsLikelySmi();
}
- if (!var->is_this() &&
- !Heap::result_symbol()->Equals(*var->name())) {
- func_name_inferrer_.PushName(var->name());
- }
-
if (FLAG_safe_int32_compiler) {
if (var->IsStackAllocated() &&
!var->is_arguments() &&
if (literal->IsSmi()) {
node->type()->SetAsLikelySmi();
node->set_side_effect_free(true);
- } else if (literal->IsString()) {
- Handle<String> lit_str(Handle<String>::cast(literal));
- if (!Heap::prototype_symbol()->Equals(*lit_str)) {
- func_name_inferrer_.PushName(lit_str);
- }
} else if (literal->IsHeapNumber()) {
if (node->to_int32()) {
// Any HeapNumber has an int32 value if it is the input to a bit op.
void AstOptimizer::VisitObjectLiteral(ObjectLiteral* node) {
for (int i = 0; i < node->properties()->length(); i++) {
- ScopedFuncNameInferrer scoped_fni(&func_name_inferrer_);
- scoped_fni.Enter();
Visit(node->properties()->at(i)->key());
Visit(node->properties()->at(i)->value());
}
void AstOptimizer::VisitAssignment(Assignment* node) {
- ScopedFuncNameInferrer scoped_fni(&func_name_inferrer_);
switch (node->op()) {
case Token::INIT_VAR:
case Token::INIT_CONST:
case Token::ASSIGN:
// No type can be infered from the general assignment.
-
- // Don't infer if it is "a = function(){...}();"-like expression.
- if (node->value()->AsCall() == NULL) {
- scoped_fni.Enter();
- }
break;
case Token::ASSIGN_BIT_OR:
case Token::ASSIGN_BIT_XOR:
void AstOptimizer::VisitCallRuntime(CallRuntime* node) {
- ScopedFuncNameInferrer scoped_fni(&func_name_inferrer_);
- if (Factory::InitializeVarGlobal_symbol()->Equals(*node->name()) &&
- node->arguments()->length() >= 2 &&
- node->arguments()->at(1)->AsFunctionLiteral() != NULL) {
- scoped_fni.Enter();
- }
OptimizeArguments(node->arguments());
}
if (FLAG_optimize_ast && !body->is_empty()) {
HistogramTimerScope timer(&Counters::ast_optimization);
- AstOptimizer optimizer(function->name());
+ AstOptimizer optimizer;
optimizer.Optimize(body);
if (optimizer.HasStackOverflow()) {
return false;