Commit 39957aa7 authored by peterhal@chromium.org's avatar peterhal@chromium.org

Issue 117 - strict mode and future reserved words

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6653 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 87233c49
......@@ -147,6 +147,7 @@ function FormatMessage(message) {
unexpected_token_number: ["Unexpected number"],
unexpected_token_string: ["Unexpected string"],
unexpected_token_identifier: ["Unexpected identifier"],
unexpected_strict_reserved: ["Unexpected strict mode reserved word"],
unexpected_eos: ["Unexpected end of input"],
malformed_regexp: ["Invalid regular expression: /", "%0", "/: ", "%1"],
unterminated_regexp: ["Invalid regular expression: missing /"],
......@@ -221,6 +222,7 @@ function FormatMessage(message) {
strict_lhs_assignment: ["Assignment to eval or arguments is not allowed in strict mode"],
strict_lhs_postfix: ["Postfix increment/decrement may not have eval or arguments operand in strict mode"],
strict_lhs_prefix: ["Prefix increment/decrement may not have eval or arguments operand in strict mode"],
strict_reserved_word: ["Use of future reserved word in strict mode"],
};
}
var message_type = %MessageGetType(message);
......
This diff is collapsed.
......@@ -548,6 +548,7 @@ class Parser {
ZoneList<Expression*>* ParseArguments(bool* ok);
FunctionLiteral* ParseFunctionLiteral(Handle<String> var_name,
bool name_is_reserved,
int function_token_position,
FunctionLiteralType type,
bool* ok);
......@@ -577,6 +578,8 @@ class Parser {
return scanner().Next();
}
bool peek_any_identifier();
INLINE(void Consume(Token::Value token));
void Expect(Token::Value token, bool* ok);
bool Check(Token::Value token);
......@@ -610,6 +613,7 @@ class Parser {
Literal* GetLiteralNumber(double value);
Handle<String> ParseIdentifier(bool* ok);
Handle<String> ParseIdentifierOrReservedWord(bool* is_reserved, bool* ok);
Handle<String> ParseIdentifierName(bool* ok);
Handle<String> ParseIdentifierOrGetOrSet(bool* is_get,
bool* is_set,
......
......@@ -83,6 +83,7 @@ void PreParser::ReportUnexpectedToken(i::Token::Value token) {
return ReportMessageAt(source_location.beg_pos, source_location.end_pos,
"unexpected_token_string", NULL);
case i::Token::IDENTIFIER:
case i::Token::FUTURE_RESERVED_WORD:
return ReportMessageAt(source_location.beg_pos, source_location.end_pos,
"unexpected_token_identifier", NULL);
default:
......@@ -790,7 +791,7 @@ PreParser::Expression PreParser::ParseMemberWithNewPrefixesExpression(
Expression result = kUnknownExpression;
if (peek() == i::Token::FUNCTION) {
Consume(i::Token::FUNCTION);
if (peek() == i::Token::IDENTIFIER) {
if (peek_any_identifier()) {
ParseIdentifier(CHECK_OK);
}
result = ParseFunctionLiteral(CHECK_OK);
......@@ -858,7 +859,8 @@ PreParser::Expression PreParser::ParsePrimaryExpression(bool* ok) {
break;
}
case i::Token::IDENTIFIER: {
case i::Token::IDENTIFIER:
case i::Token::FUTURE_RESERVED_WORD: {
ParseIdentifier(CHECK_OK);
result = kIdentifierExpression;
break;
......@@ -946,7 +948,8 @@ PreParser::Expression PreParser::ParseObjectLiteral(bool* ok) {
while (peek() != i::Token::RBRACE) {
i::Token::Value next = peek();
switch (next) {
case i::Token::IDENTIFIER: {
case i::Token::IDENTIFIER:
case i::Token::FUTURE_RESERVED_WORD: {
bool is_getter = false;
bool is_setter = false;
ParseIdentifierOrGetOrSet(&is_getter, &is_setter, CHECK_OK);
......@@ -954,6 +957,7 @@ PreParser::Expression PreParser::ParseObjectLiteral(bool* ok) {
i::Token::Value name = Next();
bool is_keyword = i::Token::IsKeyword(name);
if (name != i::Token::IDENTIFIER &&
name != i::Token::FUTURE_RESERVED_WORD &&
name != i::Token::NUMBER &&
name != i::Token::STRING &&
!is_keyword) {
......@@ -1151,7 +1155,9 @@ PreParser::Expression PreParser::GetStringSymbol() {
PreParser::Identifier PreParser::ParseIdentifier(bool* ok) {
Expect(i::Token::IDENTIFIER, ok);
if (!Check(i::Token::FUTURE_RESERVED_WORD)) {
Expect(i::Token::IDENTIFIER, ok);
}
if (!*ok) return kUnknownIdentifier;
return GetIdentifierSymbol();
}
......@@ -1166,7 +1172,8 @@ PreParser::Identifier PreParser::ParseIdentifierName(bool* ok) {
i::StrLength(keyword)));
return kUnknownExpression;
}
if (next == i::Token::IDENTIFIER) {
if (next == i::Token::IDENTIFIER ||
next == i::Token::FUTURE_RESERVED_WORD) {
return GetIdentifierSymbol();
}
*ok = false;
......@@ -1175,19 +1182,23 @@ PreParser::Identifier PreParser::ParseIdentifierName(bool* ok) {
// 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
// we can't do during preparsing.
// is 'get' or 'set'.
PreParser::Identifier PreParser::ParseIdentifierOrGetOrSet(bool* is_get,
bool* is_set,
bool* ok) {
Expect(i::Token::IDENTIFIER, CHECK_OK);
PreParser::Identifier result = ParseIdentifier(CHECK_OK);
if (scanner_->is_literal_ascii() && scanner_->literal_length() == 3) {
const char* token = scanner_->literal_ascii_string().start();
*is_get = strncmp(token, "get", 3) == 0;
*is_set = !*is_get && strncmp(token, "set", 3) == 0;
}
return GetIdentifierSymbol();
return result;
}
bool PreParser::peek_any_identifier() {
i::Token::Value next = peek();
return next == i::Token::IDENTIFIER ||
next == i::Token::FUTURE_RESERVED_WORD;
}
#undef CHECK_OK
......
......@@ -243,6 +243,8 @@ class PreParser {
return scanner_->Next();
}
bool peek_any_identifier();
void Consume(i::Token::Value token) { Next(); }
void Expect(i::Token::Value token, bool* ok) {
......
......@@ -796,25 +796,27 @@ KeywordMatcher::FirstState KeywordMatcher::first_states_[] = {
{ "break", KEYWORD_PREFIX, Token::BREAK },
{ NULL, C, Token::ILLEGAL },
{ NULL, D, Token::ILLEGAL },
{ "else", KEYWORD_PREFIX, Token::ELSE },
{ NULL, E, Token::ILLEGAL },
{ NULL, F, Token::ILLEGAL },
{ NULL, UNMATCHABLE, Token::ILLEGAL },
{ NULL, UNMATCHABLE, Token::ILLEGAL },
{ NULL, I, Token::ILLEGAL },
{ NULL, UNMATCHABLE, Token::ILLEGAL },
{ NULL, UNMATCHABLE, Token::ILLEGAL },
{ NULL, UNMATCHABLE, Token::ILLEGAL },
{ "let", KEYWORD_PREFIX, Token::FUTURE_RESERVED_WORD },
{ NULL, UNMATCHABLE, Token::ILLEGAL },
{ NULL, N, Token::ILLEGAL },
{ NULL, UNMATCHABLE, Token::ILLEGAL },
{ NULL, UNMATCHABLE, Token::ILLEGAL },
{ NULL, P, Token::ILLEGAL },
{ NULL, UNMATCHABLE, Token::ILLEGAL },
{ "return", KEYWORD_PREFIX, Token::RETURN },
{ "switch", KEYWORD_PREFIX, Token::SWITCH },
{ NULL, S, Token::ILLEGAL },
{ NULL, T, Token::ILLEGAL },
{ NULL, UNMATCHABLE, Token::ILLEGAL },
{ NULL, V, Token::ILLEGAL },
{ NULL, W, Token::ILLEGAL }
{ NULL, W, Token::ILLEGAL },
{ NULL, UNMATCHABLE, Token::ILLEGAL },
{ "yield", KEYWORD_PREFIX, Token::FUTURE_RESERVED_WORD }
};
......@@ -822,7 +824,7 @@ void KeywordMatcher::Step(unibrow::uchar input) {
switch (state_) {
case INITIAL: {
// matching the first character is the only state with significant fanout.
// Match only lower-case letters in range 'b'..'w'.
// Match only lower-case letters in range 'b'..'y'.
unsigned int offset = input - kFirstCharRangeMin;
if (offset < kFirstCharRangeLength) {
state_ = first_states_[offset].state;
......@@ -850,6 +852,8 @@ void KeywordMatcher::Step(unibrow::uchar input) {
break;
case C:
if (MatchState(input, 'a', CA)) return;
if (MatchKeywordStart(input, "class", 1,
Token::FUTURE_RESERVED_WORD)) return;
if (MatchState(input, 'o', CO)) return;
break;
case CA:
......@@ -872,6 +876,18 @@ void KeywordMatcher::Step(unibrow::uchar input) {
if (MatchKeywordStart(input, "default", 2, Token::DEFAULT)) return;
if (MatchKeywordStart(input, "delete", 2, Token::DELETE)) return;
break;
case E:
if (MatchKeywordStart(input, "else", 1, Token::ELSE)) return;
if (MatchKeywordStart(input, "enum", 1,
Token::FUTURE_RESERVED_WORD)) return;
if (MatchState(input, 'x', EX)) return;
break;
case EX:
if (MatchKeywordStart(input, "export", 2,
Token::FUTURE_RESERVED_WORD)) return;
if (MatchKeywordStart(input, "extends", 2,
Token::FUTURE_RESERVED_WORD)) return;
break;
case F:
if (MatchKeywordStart(input, "false", 1, Token::FALSE_LITERAL)) return;
if (MatchKeywordStart(input, "finally", 1, Token::FINALLY)) return;
......@@ -880,10 +896,22 @@ void KeywordMatcher::Step(unibrow::uchar input) {
break;
case I:
if (MatchKeyword(input, 'f', KEYWORD_MATCHED, Token::IF)) return;
if (MatchState(input, 'm', IM)) return;
if (MatchKeyword(input, 'n', IN, Token::IN)) return;
break;
case IM:
if (MatchState(input, 'p', IMP)) return;
break;
case IMP:
if (MatchKeywordStart(input, "implements", 3,
Token::FUTURE_RESERVED_WORD )) return;
if (MatchKeywordStart(input, "import", 3,
Token::FUTURE_RESERVED_WORD)) return;
break;
case IN:
token_ = Token::IDENTIFIER;
if (MatchKeywordStart(input, "interface", 2,
Token::FUTURE_RESERVED_WORD)) return;
if (MatchKeywordStart(input, "instanceof", 2, Token::INSTANCEOF)) return;
break;
case N:
......@@ -891,6 +919,27 @@ void KeywordMatcher::Step(unibrow::uchar input) {
if (MatchKeywordStart(input, "new", 1, Token::NEW)) return;
if (MatchKeywordStart(input, "null", 1, Token::NULL_LITERAL)) return;
break;
case P:
if (MatchKeywordStart(input, "package", 1,
Token::FUTURE_RESERVED_WORD)) return;
if (MatchState(input, 'r', PR)) return;
if (MatchKeywordStart(input, "public", 1,
Token::FUTURE_RESERVED_WORD)) return;
break;
case PR:
if (MatchKeywordStart(input, "private", 2,
Token::FUTURE_RESERVED_WORD)) return;
if (MatchKeywordStart(input, "protected", 2,
Token::FUTURE_RESERVED_WORD)) return;
break;
case S:
if (MatchKeywordStart(input, "static", 1,
Token::FUTURE_RESERVED_WORD)) return;
if (MatchKeywordStart(input, "super", 1,
Token::FUTURE_RESERVED_WORD)) return;
if (MatchKeywordStart(input, "switch", 1,
Token::SWITCH)) return;
break;
case T:
if (MatchState(input, 'h', TH)) return;
if (MatchState(input, 'r', TR)) return;
......
......@@ -564,10 +564,17 @@ class KeywordMatcher {
CON,
D,
DE,
E,
EX,
F,
I,
IM,
IMP,
IN,
N,
P,
PR,
S,
T,
TH,
TR,
......@@ -583,7 +590,7 @@ class KeywordMatcher {
// Range of possible first characters of a keyword.
static const unsigned int kFirstCharRangeMin = 'b';
static const unsigned int kFirstCharRangeMax = 'w';
static const unsigned int kFirstCharRangeMax = 'y';
static const unsigned int kFirstCharRangeLength =
kFirstCharRangeMax - kFirstCharRangeMin + 1;
// State map for first keyword character range.
......
......@@ -155,38 +155,6 @@ namespace internal {
K(WHILE, "while", 0) \
K(WITH, "with", 0) \
\
/* Future reserved words (ECMA-262, section 7.5.3, page 14). */ \
F(ABSTRACT, "abstract", 0) \
F(BOOLEAN, "boolean", 0) \
F(BYTE, "byte", 0) \
F(CHAR, "char", 0) \
F(CLASS, "class", 0) \
K(CONST, "const", 0) \
F(DOUBLE, "double", 0) \
F(ENUM, "enum", 0) \
F(EXPORT, "export", 0) \
F(EXTENDS, "extends", 0) \
F(FINAL, "final", 0) \
F(FLOAT, "float", 0) \
F(GOTO, "goto", 0) \
F(IMPLEMENTS, "implements", 0) \
F(IMPORT, "import", 0) \
F(INT, "int", 0) \
F(INTERFACE, "interface", 0) \
F(LONG, "long", 0) \
K(NATIVE, "native", 0) \
F(PACKAGE, "package", 0) \
F(PRIVATE, "private", 0) \
F(PROTECTED, "protected", 0) \
F(PUBLIC, "public", 0) \
F(SHORT, "short", 0) \
F(STATIC, "static", 0) \
F(SUPER, "super", 0) \
F(SYNCHRONIZED, "synchronized", 0) \
F(THROWS, "throws", 0) \
F(TRANSIENT, "transient", 0) \
F(VOLATILE, "volatile", 0) \
\
/* Literals (ECMA-262, section 7.8, page 16). */ \
K(NULL_LITERAL, "null", 0) \
K(TRUE_LITERAL, "true", 0) \
......@@ -197,6 +165,11 @@ namespace internal {
/* Identifiers (not keywords or future reserved words). */ \
T(IDENTIFIER, NULL, 0) \
\
/* Future reserved words (ECMA-262, section 7.6.1.2). */ \
T(FUTURE_RESERVED_WORD, NULL, 0) \
K(CONST, "const", 0) \
K(NATIVE, "native", 0) \
\
/* Illegal token - not able to scan. */ \
T(ILLEGAL, "ILLEGAL", 0) \
\
......
......@@ -271,3 +271,68 @@ CheckStrictMode("function strict() { var x = --arguments; }", SyntaxError);
var y = [void arguments, typeof arguments,
+arguments, -arguments, ~arguments, !arguments];
})();
// 7.6.1.2 Future Reserved Words
var future_reserved_words = [
"class",
"enum",
"export",
"extends",
"import",
"super",
"implements",
"interface",
"let",
"package",
"private",
"protected",
"public",
"static",
"yield" ];
function testFutureReservedWord(word) {
// Simple use of each reserved word
CheckStrictMode("var " + word + " = 1;", SyntaxError);
// object literal properties
eval("var x = { " + word + " : 42 };");
eval("var x = { get " + word + " () {} };");
eval("var x = { set " + word + " (value) {} };");
// object literal with string literal property names
eval("var x = { '" + word + "' : 42 };");
eval("var x = { get '" + word + "' () { } };");
eval("var x = { set '" + word + "' (value) { } };");
eval("var x = { get '" + word + "' () { 'use strict'; } };");
eval("var x = { set '" + word + "' (value) { 'use strict'; } };");
// Function names and arguments, strict and non-strict contexts
CheckStrictMode("function " + word + " () {}", SyntaxError);
CheckStrictMode("function foo (" + word + ") {}", SyntaxError);
CheckStrictMode("function foo (" + word + ", " + word + ") {}", SyntaxError);
CheckStrictMode("function foo (a, " + word + ") {}", SyntaxError);
CheckStrictMode("function foo (" + word + ", a) {}", SyntaxError);
CheckStrictMode("function foo (a, " + word + ", b) {}", SyntaxError);
CheckStrictMode("var foo = function (" + word + ") {}", SyntaxError);
// Function names and arguments when the body is strict
assertThrows("function " + word + " () { 'use strict'; }", SyntaxError);
assertThrows("function foo (" + word + ") 'use strict'; {}", SyntaxError);
assertThrows("function foo (" + word + ", " + word + ") { 'use strict'; }", SyntaxError);
assertThrows("function foo (a, " + word + ") { 'use strict'; }", SyntaxError);
assertThrows("function foo (" + word + ", a) { 'use strict'; }", SyntaxError);
assertThrows("function foo (a, " + word + ", b) { 'use strict'; }", SyntaxError);
assertThrows("var foo = function (" + word + ") { 'use strict'; }", SyntaxError);
// get/set when the body is strict
eval("var x = { get " + word + " () { 'use strict'; } };");
eval("var x = { set " + word + " (value) { 'use strict'; } };");
assertThrows("var x = { get foo(" + word + ") { 'use strict'; } };", SyntaxError);
assertThrows("var x = { set foo(" + word + ") { 'use strict'; } };", SyntaxError);
}
for (var i = 0; i < future_reserved_words.length; i++) {
testFutureReservedWord(future_reserved_words[i]);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment