Chromium Code Reviews| Index: src/parser.cc |
| diff --git a/src/parser.cc b/src/parser.cc |
| index 5ea1c5e083ac44a15ea4ac100f1f6848d9d4fffd..6b69fca6407a7c47e55eb14e87f4220acfd21584 100644 |
| --- a/src/parser.cc |
| +++ b/src/parser.cc |
| @@ -283,6 +283,14 @@ class TemporaryScope BASE_EMBEDDED { |
| void AddLoop() { loop_count_++; } |
| bool ContainsLoops() const { return loop_count_ > 0; } |
| + bool StrictMode() { return strict_mode_; } |
| + void EnableStrictMode() { |
| + strict_mode_ = FLAG_strict_mode; |
| + } |
| + |
| + // Checks whether scope already declares parameter with a name "param_name". |
| + bool CheckParameterDeclared(Scope *scope, Handle<String> param_name); |
| + |
| private: |
| // Captures the number of literals that need materialization in the |
| // function. Includes regexp literals, and boilerplate for object |
| @@ -300,6 +308,12 @@ class TemporaryScope BASE_EMBEDDED { |
| // Captures the number of loops inside the scope. |
| int loop_count_; |
| + // Parsing strict mode code. |
| + bool strict_mode_; |
| + |
| + // Hash map for finding duplicate parameter names |
| + HashMap* param_names_; |
| + |
| // Bookkeeping |
| TemporaryScope** variable_; |
| TemporaryScope* parent_; |
| @@ -312,16 +326,62 @@ TemporaryScope::TemporaryScope(TemporaryScope** variable) |
| only_simple_this_property_assignments_(false), |
| this_property_assignments_(Factory::empty_fixed_array()), |
| loop_count_(0), |
| + param_names_(NULL), |
| variable_(variable), |
| parent_(*variable) { |
| + // Inherit the strict mode from the parent scope. |
| + strict_mode_ = (parent_ != NULL) ? parent_->strict_mode_ : false; |
| *variable = this; |
| } |
| TemporaryScope::~TemporaryScope() { |
| + if (param_names_) { |
| +// TODO(mmaly): How to allocate temporary map and free it when not needed? |
|
Martin Maly
2011/01/14 00:06:28
I am trying to allocate the hash map only when fun
|
| +// delete param_names_; |
| + param_names_ = 0; |
| + } |
| *variable_ = parent_; |
| } |
| +bool TemporaryScope::CheckParameterDeclared( |
| + Scope *scope, Handle<String> param_name) { |
| + |
| + const int LinearLookupThreshold = 5; |
| + |
|
Martin Maly
2011/01/14 00:06:28
Moved the code from Scope to TemporaryScope since
|
| + int length = scope->num_parameters(); |
| + if (length <= LinearLookupThreshold) { |
| + for (int i = 0; i < length; i ++) { |
| + if (param_name.is_identical_to(scope->parameter(i)->name())) { |
| + // Parameter with the given name found! |
| + return true; |
| + } |
| + } |
| + } else { |
| + if (param_names_ == NULL) { |
| + // Populate the variable map. |
| + param_names_ = new VariableMap(); |
| + for (int i = 0; i < length; i ++) { |
| + Handle<String> name = scope->parameter(i)->name(); |
| + HashMap::Entry* e = param_names_->Lookup( |
| + name.location(), name->Hash(), true); |
| + ASSERT(e->value == NULL); |
| + e->value = name.location(); |
| + } |
| + } |
| + |
| + HashMap::Entry* p = param_names_->Lookup( |
| + param_name.location(), param_name->Hash(), true); |
| + if (p->value == NULL) { |
| + p->value = param_name.location(); |
| + } else { |
| + // Parameter with the given name found! |
| + return true; |
| + } |
| + } |
| + return false; |
| +} |
| + |
| Handle<String> Parser::LookupSymbol(int symbol_id) { |
| // Length of symbol cache is the number of identified symbols. |
| @@ -561,7 +621,6 @@ class LexicalScope BASE_EMBEDDED { |
| int prev_level_; |
| }; |
| - |
| // ---------------------------------------------------------------------------- |
| // The CHECK_OK macro is a convenient macro to enforce error |
| // handling for functions that may fail (by returning !*ok). |
| @@ -668,7 +727,8 @@ FunctionLiteral* Parser::DoParseProgram(Handle<String> source, |
| 0, |
| source->length(), |
| false, |
| - temp_scope.ContainsLoops()); |
| + temp_scope.ContainsLoops(), |
| + temp_scope.StrictMode()); |
| } else if (stack_overflow_) { |
| Top::StackOverflow(); |
| } |
| @@ -1058,8 +1118,45 @@ void* Parser::ParseSourceElements(ZoneList<Statement*>* processor, |
| ASSERT(processor != NULL); |
| InitializationBlockFinder block_finder; |
| ThisNamedPropertyAssigmentFinder this_property_assignment_finder; |
| + bool directive_prologue = true; // Parsing directive prologue. |
| + |
| while (peek() != end_token) { |
| - Statement* stat = ParseStatement(NULL, CHECK_OK); |
| + Statement* stat; |
| + |
| + if (directive_prologue && peek() == Token::STRING) { |
| + // A shot at a directive. |
| + Scanner::Location directive_loc = scanner().peek_location(); |
| + stat = ParseStatement(NULL, CHECK_OK); |
| + if (stat == NULL || stat->IsEmpty()) { |
| + directive_prologue = false; // End of directive prologue. |
| + continue; |
| + } |
| + |
| + if (directive_prologue) { |
| + ExpressionStatement *e_stat; |
| + Literal *literal; |
| + // Still processing directive prologue? |
| + if ((e_stat = stat->AsExpressionStatement()) != NULL && |
| + (literal = e_stat->expression()->AsLiteral()) != NULL && |
| + literal->handle()->IsString()) { |
| + Handle<String> directive = Handle<String>::cast(literal->handle()); |
| + |
| + // Check "use strict" directive (ES5 14.1). |
| + if (!temp_scope_->StrictMode() && |
| + directive->Equals(Heap::use_strict()) && |
| + directive_loc.end_pos - directive_loc.beg_pos == |
| + Heap::use_strict()->length() + 2) { |
| + temp_scope_->EnableStrictMode(); |
| + } |
| + } else { |
| + // End of the directive prologue. |
| + directive_prologue = false; |
| + } |
| + } |
| + } else { |
| + stat = ParseStatement(NULL, CHECK_OK); |
| + } |
| + |
| if (stat == NULL || stat->IsEmpty()) continue; |
| // We find and mark the initialization blocks on top level code only. |
| // This is because the optimization prevents reuse of the map transitions, |
| @@ -1414,6 +1511,10 @@ Block* Parser::ParseVariableStatement(bool* ok) { |
| return result; |
| } |
| +static bool IsEvalOrArguments(Handle<String> string) { |
| + return string.is_identical_to(Factory::eval_symbol()) || |
| + string.is_identical_to(Factory::arguments_symbol()); |
| +} |
| // If the variable declaration declares exactly one non-const |
| // variable, then *var is set to that variable. In all other cases, |
| @@ -1462,6 +1563,13 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN, |
| Handle<String> name = ParseIdentifier(CHECK_OK); |
| if (fni_ != NULL) fni_->PushVariableName(name); |
| + // Strict mode variables may not be named eval or arguments |
| + if (temp_scope_->StrictMode() && IsEvalOrArguments(name)) { |
| + ReportMessage("strict_var_name", Vector<const char*>::empty()); |
| + *ok = false; |
| + return NULL; |
| + } |
| + |
| // Declare variable. |
| // Note that we *always* must treat the initial value via a separate init |
| // assignment for variables and constants because the value must be assigned |
| @@ -1814,6 +1922,13 @@ Statement* Parser::ParseWithStatement(ZoneStringList* labels, bool* ok) { |
| // 'with' '(' Expression ')' Statement |
| Expect(Token::WITH, CHECK_OK); |
| + |
| + if (temp_scope_->StrictMode()) { |
| + ReportMessage("strict_mode_with", Vector<const char*>::empty()); |
| + *ok = false; |
| + return NULL; |
| + } |
| + |
| Expect(Token::LPAREN, CHECK_OK); |
| Expression* expr = ParseExpression(true, CHECK_OK); |
| Expect(Token::RPAREN, CHECK_OK); |
| @@ -1946,6 +2061,13 @@ TryStatement* Parser::ParseTryStatement(bool* ok) { |
| Expect(Token::LPAREN, CHECK_OK); |
| Handle<String> name = ParseIdentifier(CHECK_OK); |
| + |
| + if (temp_scope_->StrictMode() && IsEvalOrArguments(name)) { |
| + ReportMessage("strict_catch_variable", Vector<const char*>::empty()); |
| + *ok = false; |
| + return NULL; |
| + } |
| + |
| Expect(Token::RPAREN, CHECK_OK); |
| if (peek() == Token::LBRACE) { |
| @@ -3192,11 +3314,27 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name, |
| // '(' (Identifier)*[','] ')' |
| Expect(Token::LPAREN, CHECK_OK); |
| int start_pos = scanner().location().beg_pos; |
| + Scanner::Location eval_loc(RelocInfo::kNoPosition, RelocInfo::kNoPosition); |
| + Scanner::Location dupe_loc(RelocInfo::kNoPosition, RelocInfo::kNoPosition); |
| + |
| bool done = (peek() == Token::RPAREN); |
| while (!done) { |
| Handle<String> param_name = ParseIdentifier(CHECK_OK); |
| - top_scope_->AddParameter(top_scope_->DeclareLocal(param_name, |
| - Variable::VAR)); |
| + Variable* parameter = top_scope_->DeclareLocal(param_name, Variable::VAR); |
| + |
|
Martin Maly
2011/01/14 00:06:28
This code runs even when not processing strict mod
|
| + // Store locations for possible future error reports. |
| + if (eval_loc.beg_pos == RelocInfo::kNoPosition && |
| + IsEvalOrArguments(param_name)) { |
| + // Store location for later |
| + eval_loc = scanner().location(); |
| + } |
| + if (dupe_loc.beg_pos == RelocInfo::kNoPosition && |
| + temp_scope.CheckParameterDeclared(scope, param_name)) { |
| + // Store location for later |
| + dupe_loc = scanner().location(); |
| + } |
| + |
| + top_scope_->AddParameter(parameter); |
| num_parameters++; |
| done = (peek() == Token::RPAREN); |
| if (!done) Expect(Token::COMMA, CHECK_OK); |
| @@ -3265,6 +3403,32 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name, |
| end_pos = scanner().location().end_pos; |
| } |
| + // Validate strict mode. |
| + if (temp_scope_->StrictMode()) { |
| + if (IsEvalOrArguments(name)) { |
| + int position = function_token_position != RelocInfo::kNoPosition |
|
Martin Maly
2011/01/14 00:06:28
This is still necessary because property get/set g
|
| + ? function_token_position |
| + : (start_pos > 0 ? start_pos - 1 : start_pos); |
| + ReportMessageAt(Scanner::Location(position, start_pos), |
| + "strict_function_name", Vector<const char*>::empty()); |
| + *ok = false; |
| + return NULL; |
| + } |
| + if (eval_loc.beg_pos != RelocInfo::kNoPosition) { |
| + ReportMessageAt(eval_loc, "strict_param_name", |
| + Vector<const char*>::empty()); |
| + *ok = false; |
| + return NULL; |
| + } |
| + if (dupe_loc.beg_pos != RelocInfo::kNoPosition) { |
| + ReportMessageAt(dupe_loc, "strict_param_dupe", |
| + Vector<const char*>::empty()); |
| + *ok = false; |
| + return NULL; |
| + } |
| + // TODO(mmaly): Check for octal escape sequence here. |
| + } |
| + |
| FunctionLiteral* function_literal = |
| new FunctionLiteral(name, |
| top_scope_, |
| @@ -3277,7 +3441,8 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name, |
| start_pos, |
| end_pos, |
| function_name->length() > 0, |
| - temp_scope.ContainsLoops()); |
| + temp_scope.ContainsLoops(), |
| + temp_scope.StrictMode()); |
| function_literal->set_function_token_position(function_token_position); |
| if (fni_ != NULL && !is_named) fni_->AddFunction(function_literal); |