Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(685)

Unified Diff: src/parser.cc

Issue 6597029: [Isolates] Merge r 6300:6500 from bleeding_edge to isolates. (Closed) Base URL: http://v8.googlecode.com/svn/branches/experimental/isolates/
Patch Set: Created 9 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/parser.h ('k') | src/platform.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/parser.cc
===================================================================
--- src/parser.cc (revision 6955)
+++ src/parser.cc (working copy)
@@ -283,6 +283,11 @@
void AddLoop() { loop_count_++; }
bool ContainsLoops() const { return loop_count_ > 0; }
+ bool StrictMode() { return strict_mode_; }
+ void EnableStrictMode() {
+ strict_mode_ = FLAG_strict_mode;
+ }
+
private:
// Captures the number of literals that need materialization in the
// function. Includes regexp literals, and boilerplate for object
@@ -300,6 +305,9 @@
// Captures the number of loops inside the scope.
int loop_count_;
+ // Parsing strict mode code.
+ bool strict_mode_;
+
// Bookkeeping
TemporaryScope** variable_;
TemporaryScope* parent_;
@@ -315,6 +323,8 @@
loop_count_(0),
variable_(variable),
parent_(*variable) {
+ // Inherit the strict mode from the parent scope.
+ strict_mode_ = (parent_ != NULL) && parent_->strict_mode_;
*variable = this;
}
@@ -566,7 +576,6 @@
int prev_level_;
};
-
// ----------------------------------------------------------------------------
// The CHECK_OK macro is a convenient macro to enforce error
// handling for functions that may fail (by returning !*ok).
@@ -606,7 +615,8 @@
extension_(extension),
pre_data_(pre_data),
fni_(NULL),
- stack_overflow_(false) {
+ stack_overflow_(false),
+ parenthesized_function_(false) {
AstNode::ResetIds();
}
@@ -660,7 +670,11 @@
TemporaryScope temp_scope(&this->temp_scope_);
ZoneList<Statement*>* body = new ZoneList<Statement*>(16);
bool ok = true;
+ int beg_loc = scanner().location().beg_pos;
ParseSourceElements(body, Token::EOS, &ok);
+ if (ok && temp_scope_->StrictMode()) {
+ CheckOctalLiteral(beg_loc, scanner().location().end_pos, &ok);
+ }
if (ok) {
result = new FunctionLiteral(
no_name,
@@ -674,7 +688,8 @@
0,
source->length(),
false,
- temp_scope.ContainsLoops());
+ temp_scope.ContainsLoops(),
+ temp_scope.StrictMode());
} else if (stack_overflow_) {
isolate()->StackOverflow();
}
@@ -783,7 +798,8 @@
const char* type,
Vector<const char*> args) {
MessageLocation location(script_,
- source_location.beg_pos, source_location.end_pos);
+ source_location.beg_pos,
+ source_location.end_pos);
Handle<JSArray> array = isolate()->factory()->NewJSArray(args.length());
for (int i = 0; i < args.length(); i++) {
SetElement(array, i,
@@ -794,6 +810,21 @@
}
+void Parser::ReportMessageAt(Scanner::Location source_location,
+ const char* type,
+ Vector<Handle<String> > args) {
+ MessageLocation location(script_,
+ source_location.beg_pos,
+ source_location.end_pos);
+ Handle<JSArray> array = FACTORY->NewJSArray(args.length());
+ for (int i = 0; i < args.length(); i++) {
+ SetElement(array, i, args[i]);
+ }
+ Handle<Object> result = FACTORY->NewSyntaxError(type, array);
+ isolate()->Throw(*result, &location);
+}
+
+
// Base class containing common code for the different finder classes used by
// the parser.
class ParserFinder {
@@ -1065,9 +1096,46 @@
ASSERT(processor != NULL);
InitializationBlockFinder block_finder;
ThisNamedPropertyAssigmentFinder this_property_assignment_finder;
+ bool directive_prologue = true; // Parsing directive prologue.
+
while (peek() != end_token) {
+ if (directive_prologue && peek() != Token::STRING) {
+ directive_prologue = false;
+ }
+
+ Scanner::Location token_loc = scanner().peek_location();
Statement* stat = ParseStatement(NULL, CHECK_OK);
- if (stat == NULL || stat->IsEmpty()) continue;
+
+ if (stat == NULL || stat->IsEmpty()) {
+ directive_prologue = false; // End of directive prologue.
+ continue;
+ }
+
+ if (directive_prologue) {
+ // A shot at a directive.
+ 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(isolate()->heap()->use_strict()) &&
+ token_loc.end_pos - token_loc.beg_pos ==
+ isolate()->heap()->use_strict()->length() + 2) {
+ temp_scope_->EnableStrictMode();
+ // "use strict" is the only directive for now.
+ directive_prologue = false;
+ }
+ } else {
+ // End of the directive prologue.
+ directive_prologue = false;
+ }
+ }
+
// We find and mark the initialization blocks on top level 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.
@@ -1421,6 +1489,10 @@
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,
@@ -1469,6 +1541,13 @@
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
@@ -1699,14 +1778,16 @@
IterationStatement* target = NULL;
target = LookupContinueTarget(label, CHECK_OK);
if (target == NULL) {
- // Illegal continue statement. To be consistent with KJS we delay
- // reporting of the syntax error until runtime.
- Handle<String> error_type = isolate()->factory()->illegal_continue_symbol();
+ // Illegal continue statement.
+ const char* message = "illegal_continue";
+ Vector<Handle<String> > args;
if (!label.is_null()) {
- error_type = isolate()->factory()->unknown_label_symbol();
+ message = "unknown_label";
+ args = Vector<Handle<String> >(&label, 1);
}
- Expression* throw_error = NewThrowSyntaxError(error_type, label);
- return new ExpressionStatement(throw_error);
+ ReportMessageAt(scanner().location(), message, args);
+ *ok = false;
+ return NULL;
}
ExpectSemicolon(CHECK_OK);
return new ContinueStatement(target);
@@ -1732,14 +1813,16 @@
BreakableStatement* target = NULL;
target = LookupBreakTarget(label, CHECK_OK);
if (target == NULL) {
- // Illegal break statement. To be consistent with KJS we delay
- // reporting of the syntax error until runtime.
- Handle<String> error_type = isolate()->factory()->illegal_break_symbol();
+ // Illegal break statement.
+ const char* message = "illegal_break";
+ Vector<Handle<String> > args;
if (!label.is_null()) {
- error_type = isolate()->factory()->unknown_label_symbol();
+ message = "unknown_label";
+ args = Vector<Handle<String> >(&label, 1);
}
- Expression* throw_error = NewThrowSyntaxError(error_type, label);
- return new ExpressionStatement(throw_error);
+ ReportMessageAt(scanner().location(), message, args);
+ *ok = false;
+ return NULL;
}
ExpectSemicolon(CHECK_OK);
return new BreakStatement(target);
@@ -1825,6 +1908,13 @@
// '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);
@@ -1957,6 +2047,13 @@
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) {
@@ -2498,9 +2595,13 @@
// The calls that need special treatment are the
// direct (i.e. not aliased) eval calls. These calls are all of the
// form eval(...) with no explicit receiver object where eval is not
- // declared in the current scope chain. These calls are marked as
- // potentially direct eval calls. Whether they are actually direct calls
- // to eval is determined at run time.
+ // declared in the current scope chain.
+ // These calls are marked as potentially direct eval calls. Whether
+ // they are actually direct calls to eval is determined at run time.
+ // TODO(994): In ES5, it doesn't matter if the "eval" var is declared
+ // in the local scope chain. It only matters that it's called "eval",
+ // is called without a receiver and it refers to the original eval
+ // function.
VariableProxy* callee = result->AsVariableProxy();
if (callee != NULL &&
callee->IsVariable(isolate()->factory()->eval_symbol())) {
@@ -2751,6 +2852,9 @@
case Token::LPAREN:
Consume(Token::LPAREN);
+ // Heuristically try to detect immediately called functions before
+ // seeing the call parentheses.
+ parenthesized_function_ = (peek() == Token::FUNCTION);
result = ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
break;
@@ -2925,7 +3029,127 @@
return isolate()->factory()->undefined_value();
}
+// Defined in ast.cc
+bool IsEqualString(void* first, void* second);
+bool IsEqualSmi(void* first, void* second);
+
+// Validation per 11.1.5 Object Initialiser
+class ObjectLiteralPropertyChecker {
+ public:
+ ObjectLiteralPropertyChecker(Parser* parser, bool strict) :
+ props(&IsEqualString),
+ elems(&IsEqualSmi),
+ parser_(parser),
+ strict_(strict) {
+ }
+
+ void CheckProperty(
+ ObjectLiteral::Property* property,
+ Scanner::Location loc,
+ bool* ok);
+
+ private:
+ enum PropertyKind {
+ kGetAccessor = 0x01,
+ kSetAccessor = 0x02,
+ kAccessor = kGetAccessor | kSetAccessor,
+ kData = 0x04
+ };
+
+ static intptr_t GetPropertyKind(ObjectLiteral::Property* property) {
+ switch (property->kind()) {
+ case ObjectLiteral::Property::GETTER:
+ return kGetAccessor;
+ case ObjectLiteral::Property::SETTER:
+ return kSetAccessor;
+ default:
+ return kData;
+ }
+ }
+
+ HashMap props;
+ HashMap elems;
+ Parser* parser_;
+ bool strict_;
+};
+
+
+void ObjectLiteralPropertyChecker::CheckProperty(
+ ObjectLiteral::Property* property,
+ Scanner::Location loc,
+ bool* ok) {
+
+ ASSERT(property != NULL);
+
+ Literal *lit = property->key();
+ Handle<Object> handle = lit->handle();
+
+ uint32_t hash;
+ HashMap* map;
+ void* key;
+ Smi* smi_key_location;
+
+ if (handle->IsSymbol()) {
+ Handle<String> name(String::cast(*handle));
+ if (name->AsArrayIndex(&hash)) {
+ smi_key_location = Smi::FromInt(hash);
+ key = &smi_key_location;
+ map = &elems;
+ } else {
+ key = handle.location();
+ hash = name->Hash();
+ map = &props;
+ }
+ } else if (handle->ToArrayIndex(&hash)) {
+ key = handle.location();
+ map = &elems;
+ } else {
+ ASSERT(handle->IsNumber());
+ double num = handle->Number();
+ char arr[100];
+ Vector<char> buffer(arr, ARRAY_SIZE(arr));
+ const char* str = DoubleToCString(num, buffer);
+ Handle<String> name = FACTORY->NewStringFromAscii(CStrVector(str));
+ key = name.location();
+ hash = name->Hash();
+ map = &props;
+ }
+
+ // Lookup property previously defined, if any.
+ HashMap::Entry* entry = map->Lookup(key, hash, true);
+ intptr_t prev = reinterpret_cast<intptr_t> (entry->value);
+ intptr_t curr = GetPropertyKind(property);
+
+ // Duplicate data properties are illegal in strict mode.
+ if (strict_ && (curr & prev & kData) != 0) {
+ parser_->ReportMessageAt(loc, "strict_duplicate_property",
+ Vector<const char*>::empty());
+ *ok = false;
+ return;
+ }
+ // Data property conflicting with an accessor.
+ if (((curr & kData) && (prev & kAccessor)) ||
+ ((prev & kData) && (curr & kAccessor))) {
+ parser_->ReportMessageAt(loc, "accessor_data_property",
+ Vector<const char*>::empty());
+ *ok = false;
+ return;
+ }
+ // Two accessors of the same type conflicting
+ if ((curr & prev & kAccessor) != 0) {
+ parser_->ReportMessageAt(loc, "accessor_get_set",
+ Vector<const char*>::empty());
+ *ok = false;
+ return;
+ }
+
+ // Update map
+ entry->value = reinterpret_cast<void*> (prev | curr);
+ *ok = true;
+}
+
+
void Parser::BuildObjectLiteralConstantProperties(
ZoneList<ObjectLiteral::Property*>* properties,
Handle<FixedArray> constant_properties,
@@ -3030,12 +3254,20 @@
new ZoneList<ObjectLiteral::Property*>(4);
int number_of_boilerplate_properties = 0;
+ ObjectLiteralPropertyChecker checker(this, temp_scope_->StrictMode());
+
Expect(Token::LBRACE, CHECK_OK);
+ Scanner::Location loc = scanner().location();
+
while (peek() != Token::RBRACE) {
if (fni_ != NULL) fni_->Enter();
Literal* key = NULL;
Token::Value next = peek();
+
+ // Location of the property name token
+ Scanner::Location loc = scanner().peek_location();
+
switch (next) {
case Token::IDENTIFIER: {
bool is_getter = false;
@@ -3045,11 +3277,15 @@
if (fni_ != NULL) fni_->PushLiteralName(id);
if ((is_getter || is_setter) && peek() != Token::COLON) {
+ // Update loc to point to the identifier
+ loc = scanner().peek_location();
ObjectLiteral::Property* property =
ParseObjectLiteralGetSet(is_getter, CHECK_OK);
if (IsBoilerplateProperty(property)) {
number_of_boilerplate_properties++;
}
+ // Validate the property.
+ checker.CheckProperty(property, loc, CHECK_OK);
properties->Add(property);
if (peek() != Token::RBRACE) Expect(Token::COMMA, CHECK_OK);
@@ -3106,6 +3342,8 @@
// Count CONSTANT or COMPUTED properties to maintain the enumeration order.
if (IsBoilerplateProperty(property)) number_of_boilerplate_properties++;
+ // Validate the property
+ checker.CheckProperty(property, loc, CHECK_OK);
properties->Add(property);
// TODO(1240767): Consider allowing trailing comma.
@@ -3117,6 +3355,7 @@
}
}
Expect(Token::RBRACE, CHECK_OK);
+
// Computation of literal_index must happen before pre parse bailout.
int literal_index = temp_scope_->NextMaterializedLiteralIndex();
@@ -3210,11 +3449,23 @@
// '(' (Identifier)*[','] ')'
Expect(Token::LPAREN, CHECK_OK);
int start_pos = scanner().location().beg_pos;
+ Scanner::Location name_loc = Scanner::NoLocation();
+ Scanner::Location dupe_loc = Scanner::NoLocation();
+
bool done = (peek() == Token::RPAREN);
while (!done) {
Handle<String> param_name = ParseIdentifier(CHECK_OK);
- top_scope_->AddParameter(top_scope_->DeclareLocal(param_name,
- Variable::VAR));
+
+ // Store locations for possible future error reports.
+ if (!name_loc.IsValid() && IsEvalOrArguments(param_name)) {
+ name_loc = scanner().location();
+ }
+ if (!dupe_loc.IsValid() && top_scope_->IsDeclared(param_name)) {
+ dupe_loc = scanner().location();
+ }
+
+ Variable* parameter = top_scope_->DeclareLocal(param_name, Variable::VAR);
+ top_scope_->AddParameter(parameter);
num_parameters++;
done = (peek() == Token::RPAREN);
if (!done) Expect(Token::COMMA, CHECK_OK);
@@ -3243,8 +3494,11 @@
// Determine if the function will be lazily compiled. The mode can
// only be PARSE_LAZILY if the --lazy flag is true.
- bool is_lazily_compiled =
- mode() == PARSE_LAZILY && top_scope_->HasTrivialOuterContext();
+ bool is_lazily_compiled = (mode() == PARSE_LAZILY &&
+ top_scope_->outer_scope()->is_global_scope() &&
+ top_scope_->HasTrivialOuterContext() &&
+ !parenthesized_function_);
+ parenthesized_function_ = false; // The bit was set for this function only.
int function_block_pos = scanner().location().beg_pos;
int materialized_literal_count;
@@ -3284,6 +3538,32 @@
end_pos = scanner().location().end_pos;
}
+ // Validate strict mode.
+ if (temp_scope_->StrictMode()) {
+ if (IsEvalOrArguments(name)) {
+ int position = function_token_position != RelocInfo::kNoPosition
+ ? 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 (name_loc.IsValid()) {
+ ReportMessageAt(name_loc, "strict_param_name",
+ Vector<const char*>::empty());
+ *ok = false;
+ return NULL;
+ }
+ if (dupe_loc.IsValid()) {
+ ReportMessageAt(dupe_loc, "strict_param_dupe",
+ Vector<const char*>::empty());
+ *ok = false;
+ return NULL;
+ }
+ CheckOctalLiteral(start_pos, end_pos, CHECK_OK);
+ }
+
FunctionLiteral* function_literal =
new FunctionLiteral(name,
top_scope_,
@@ -3296,7 +3576,8 @@
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);
@@ -3426,7 +3707,19 @@
return GetSymbol(ok);
}
+// Checks whether octal literal last seen is between beg_pos and end_pos.
+// If so, reports an error.
+void Parser::CheckOctalLiteral(int beg_pos, int end_pos, bool* ok) {
+ int octal = scanner().octal_position();
+ if (beg_pos <= octal && octal <= end_pos) {
+ ReportMessageAt(Scanner::Location(octal, octal + 1), "strict_octal_literal",
+ Vector<const char*>::empty());
+ scanner().clear_octal_position();
+ *ok = false;
+ }
+}
+
// This function reads an identifier and determines whether or not it
// is 'get' or 'set'. The reason for not using ParseIdentifier and
// checking on the output is that this involves heap allocation which
« no previous file with comments | « src/parser.h ('k') | src/platform.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698