Commit 2fda95eb authored by rossberg@chromium.org's avatar rossberg@chromium.org

Make stray 'return' an early error

As required by the spec, and implemented by other browsers.

(Plus minor clean-up for redeclaration TypeErrors.)

R=marja@chromium.org
BUG=
LOG=Y

Review URL: https://codereview.chromium.org/220473014

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@20434 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 511edabe
...@@ -47,7 +47,8 @@ var kMessages = { ...@@ -47,7 +47,8 @@ var kMessages = {
incompatible_method_receiver: ["Method ", "%0", " called on incompatible receiver ", "%1"], incompatible_method_receiver: ["Method ", "%0", " called on incompatible receiver ", "%1"],
multiple_defaults_in_switch: ["More than one default clause in switch statement"], multiple_defaults_in_switch: ["More than one default clause in switch statement"],
newline_after_throw: ["Illegal newline after throw"], newline_after_throw: ["Illegal newline after throw"],
redeclaration: ["%0", " '", "%1", "' has already been declared"], label_redeclaration: ["Label '", "%0", "' has already been declared"],
var_redeclaration: ["Identifier '", "%0", "' has already been declared"],
no_catch_or_finally: ["Missing catch or finally after try"], no_catch_or_finally: ["Missing catch or finally after try"],
unknown_label: ["Undefined label '", "%0", "'"], unknown_label: ["Undefined label '", "%0", "'"],
uncaught_exception: ["Uncaught ", "%0"], uncaught_exception: ["Uncaught ", "%0"],
......
...@@ -590,8 +590,7 @@ Expression* ParserTraits::BuildUnaryExpression( ...@@ -590,8 +590,7 @@ Expression* ParserTraits::BuildUnaryExpression(
} }
Expression* ParserTraits::NewThrowReferenceError( Expression* ParserTraits::NewThrowReferenceError(const char* message, int pos) {
const char* message, int pos) {
return NewThrowError( return NewThrowError(
parser_->isolate()->factory()->MakeReferenceError_string(), parser_->isolate()->factory()->MakeReferenceError_string(),
message, HandleVector<Object>(NULL, 0), pos); message, HandleVector<Object>(NULL, 0), pos);
...@@ -609,11 +608,9 @@ Expression* ParserTraits::NewThrowSyntaxError( ...@@ -609,11 +608,9 @@ Expression* ParserTraits::NewThrowSyntaxError(
Expression* ParserTraits::NewThrowTypeError( Expression* ParserTraits::NewThrowTypeError(
const char* message, Handle<Object> arg1, Handle<Object> arg2, int pos) { const char* message, Handle<Object> arg, int pos) {
ASSERT(!arg1.is_null() && !arg2.is_null()); int argc = arg.is_null() ? 0 : 1;
Handle<Object> elements[] = { arg1, arg2 }; Vector< Handle<Object> > arguments = HandleVector<Object>(&arg, argc);
Vector< Handle<Object> > arguments =
HandleVector<Object>(elements, ARRAY_SIZE(elements));
return NewThrowError( return NewThrowError(
parser_->isolate()->factory()->MakeTypeError_string(), parser_->isolate()->factory()->MakeTypeError_string(),
message, arguments, pos); message, arguments, pos);
...@@ -1754,18 +1751,14 @@ void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) { ...@@ -1754,18 +1751,14 @@ void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) {
// In harmony we treat re-declarations as early errors. See // In harmony we treat re-declarations as early errors. See
// ES5 16 for a definition of early errors. // ES5 16 for a definition of early errors.
SmartArrayPointer<char> c_string = name->ToCString(DISALLOW_NULLS); SmartArrayPointer<char> c_string = name->ToCString(DISALLOW_NULLS);
const char* elms[2] = { "Variable", c_string.get() }; const char* elms[1] = { c_string.get() };
Vector<const char*> args(elms, 2); Vector<const char*> args(elms, 1);
ReportMessage("redeclaration", args); ReportMessage("var_redeclaration", args);
*ok = false; *ok = false;
return; return;
} }
Handle<String> message_string = Expression* expression = NewThrowTypeError(
isolate()->factory()->InternalizeOneByteString( "var_redeclaration", name, declaration->position());
STATIC_ASCII_VECTOR("Variable"));
Expression* expression =
NewThrowTypeError("redeclaration",
message_string, name, declaration->position());
declaration_scope->SetIllegalRedeclaration(expression); declaration_scope->SetIllegalRedeclaration(expression);
} }
} }
...@@ -2374,9 +2367,9 @@ Statement* Parser::ParseExpressionOrLabelledStatement(ZoneStringList* labels, ...@@ -2374,9 +2367,9 @@ Statement* Parser::ParseExpressionOrLabelledStatement(ZoneStringList* labels,
// make later anyway so we should go back and fix this then. // make later anyway so we should go back and fix this then.
if (ContainsLabel(labels, label) || TargetStackContainsLabel(label)) { if (ContainsLabel(labels, label) || TargetStackContainsLabel(label)) {
SmartArrayPointer<char> c_string = label->ToCString(DISALLOW_NULLS); SmartArrayPointer<char> c_string = label->ToCString(DISALLOW_NULLS);
const char* elms[2] = { "Label", c_string.get() }; const char* elms[1] = { c_string.get() };
Vector<const char*> args(elms, 2); Vector<const char*> args(elms, 1);
ReportMessage("redeclaration", args); ReportMessage("label_redeclaration", args);
*ok = false; *ok = false;
return NULL; return NULL;
} }
...@@ -2521,7 +2514,7 @@ Statement* Parser::ParseReturnStatement(bool* ok) { ...@@ -2521,7 +2514,7 @@ Statement* Parser::ParseReturnStatement(bool* ok) {
// reporting any errors on it, because of the way errors are // reporting any errors on it, because of the way errors are
// reported (underlining). // reported (underlining).
Expect(Token::RETURN, CHECK_OK); Expect(Token::RETURN, CHECK_OK);
int pos = position(); Scanner::Location loc = scanner()->location();
Token::Value tok = peek(); Token::Value tok = peek();
Statement* result; Statement* result;
...@@ -2539,23 +2532,17 @@ Statement* Parser::ParseReturnStatement(bool* ok) { ...@@ -2539,23 +2532,17 @@ Statement* Parser::ParseReturnStatement(bool* ok) {
Expression* generator = factory()->NewVariableProxy( Expression* generator = factory()->NewVariableProxy(
function_state_->generator_object_variable()); function_state_->generator_object_variable());
Expression* yield = factory()->NewYield( Expression* yield = factory()->NewYield(
generator, return_value, Yield::FINAL, pos); generator, return_value, Yield::FINAL, loc.beg_pos);
result = factory()->NewExpressionStatement(yield, pos); result = factory()->NewExpressionStatement(yield, loc.beg_pos);
} else { } else {
result = factory()->NewReturnStatement(return_value, pos); result = factory()->NewReturnStatement(return_value, loc.beg_pos);
} }
// An ECMAScript program is considered syntactically incorrect if it Scope* decl_scope = scope_->DeclarationScope();
// contains a return statement that is not within the body of a if (decl_scope->is_global_scope() || decl_scope->is_eval_scope()) {
// function. See ECMA-262, section 12.9, page 67. ReportMessageAt(loc, "illegal_return");
// *ok = false;
// To be consistent with KJS we report the syntax error at runtime. return NULL;
Scope* declaration_scope = scope_->DeclarationScope();
if (declaration_scope->is_global_scope() ||
declaration_scope->is_eval_scope()) {
Expression* throw_error =
NewThrowSyntaxError("illegal_return", Handle<Object>::null(), pos);
return factory()->NewExpressionStatement(throw_error, pos);
} }
return result; return result;
} }
...@@ -3669,13 +3656,13 @@ void Parser::CheckConflictingVarDeclarations(Scope* scope, bool* ok) { ...@@ -3669,13 +3656,13 @@ void Parser::CheckConflictingVarDeclarations(Scope* scope, bool* ok) {
// errors. See ES5 16 for a definition of early errors. // errors. See ES5 16 for a definition of early errors.
Handle<String> name = decl->proxy()->name(); Handle<String> name = decl->proxy()->name();
SmartArrayPointer<char> c_string = name->ToCString(DISALLOW_NULLS); SmartArrayPointer<char> c_string = name->ToCString(DISALLOW_NULLS);
const char* elms[2] = { "Variable", c_string.get() }; const char* elms[1] = { c_string.get() };
Vector<const char*> args(elms, 2); Vector<const char*> args(elms, 1);
int position = decl->proxy()->position(); int position = decl->proxy()->position();
Scanner::Location location = position == RelocInfo::kNoPosition Scanner::Location location = position == RelocInfo::kNoPosition
? Scanner::Location::invalid() ? Scanner::Location::invalid()
: Scanner::Location(position, position + 1); : Scanner::Location(position, position + 1);
ParserTraits::ReportMessageAt(location, "redeclaration", args); ParserTraits::ReportMessageAt(location, "var_redeclaration", args);
*ok = false; *ok = false;
} }
} }
......
...@@ -538,8 +538,7 @@ class ParserTraits { ...@@ -538,8 +538,7 @@ class ParserTraits {
// Generate AST node that throws a TypeError with the given // Generate AST node that throws a TypeError with the given
// type. Both arguments must be non-null (in the handle sense). // type. Both arguments must be non-null (in the handle sense).
Expression* NewThrowTypeError( Expression* NewThrowTypeError(const char* type, Handle<Object> arg, int pos);
const char* type, Handle<Object> arg1, Handle<Object> arg2, int pos);
// Generic AST generator for throwing errors from compiled code. // Generic AST generator for throwing errors from compiled code.
Expression* NewThrowError( Expression* NewThrowError(
......
...@@ -582,7 +582,7 @@ PreParser::Statement PreParser::ParseReturnStatement(bool* ok) { ...@@ -582,7 +582,7 @@ PreParser::Statement PreParser::ParseReturnStatement(bool* ok) {
// ReturnStatement :: // ReturnStatement ::
// 'return' [no line terminator] Expression? ';' // 'return' [no line terminator] Expression? ';'
// Consume the return token. It is necessary to do the before // Consume the return token. It is necessary to do before
// reporting any errors on it, because of the way errors are // reporting any errors on it, because of the way errors are
// reported (underlining). // reported (underlining).
Expect(Token::RETURN, CHECK_OK); Expect(Token::RETURN, CHECK_OK);
......
...@@ -922,7 +922,7 @@ class PreParserTraits { ...@@ -922,7 +922,7 @@ class PreParserTraits {
return PreParserExpression::Default(); return PreParserExpression::Default();
} }
PreParserExpression NewThrowTypeError( PreParserExpression NewThrowTypeError(
const char* type, Handle<Object> arg1, Handle<Object> arg2, int pos) { const char* type, Handle<Object> arg, int pos) {
return PreParserExpression::Default(); return PreParserExpression::Default();
} }
......
...@@ -2117,15 +2117,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetAccessorProperty) { ...@@ -2117,15 +2117,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetAccessorProperty) {
} }
static Failure* ThrowRedeclarationError(Isolate* isolate, static Failure* ThrowRedeclarationError(Isolate* isolate, Handle<String> name) {
const char* type, HandleScope scope(isolate);
Handle<String> name) { Handle<Object> args[1] = { name };
HandleScope scope(isolate); Handle<Object> error = isolate->factory()->NewTypeError(
Handle<Object> type_handle = "var_redeclaration", HandleVector(args, 1));
isolate->factory()->NewStringFromAscii(CStrVector(type));
Handle<Object> args[2] = { type_handle, name };
Handle<Object> error =
isolate->factory()->NewTypeError("redeclaration", HandleVector(args, 2));
return isolate->Throw(*error); return isolate->Throw(*error);
} }
...@@ -2202,7 +2198,7 @@ RUNTIME_FUNCTION(MaybeObject*, RuntimeHidden_DeclareGlobals) { ...@@ -2202,7 +2198,7 @@ RUNTIME_FUNCTION(MaybeObject*, RuntimeHidden_DeclareGlobals) {
if (lookup.IsFound() && lookup.IsDontDelete()) { if (lookup.IsFound() && lookup.IsDontDelete()) {
if (lookup.IsReadOnly() || lookup.IsDontEnum() || if (lookup.IsReadOnly() || lookup.IsDontEnum() ||
lookup.IsPropertyCallbacks()) { lookup.IsPropertyCallbacks()) {
return ThrowRedeclarationError(isolate, "function", name); return ThrowRedeclarationError(isolate, name);
} }
// If the existing property is not configurable, keep its attributes. // If the existing property is not configurable, keep its attributes.
attr = lookup.GetAttributes(); attr = lookup.GetAttributes();
...@@ -2254,8 +2250,7 @@ RUNTIME_FUNCTION(MaybeObject*, RuntimeHidden_DeclareContextSlot) { ...@@ -2254,8 +2250,7 @@ RUNTIME_FUNCTION(MaybeObject*, RuntimeHidden_DeclareContextSlot) {
if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) { if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
// Functions are not read-only. // Functions are not read-only.
ASSERT(mode != READ_ONLY || initial_value->IsTheHole()); ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var"; return ThrowRedeclarationError(isolate, name);
return ThrowRedeclarationError(isolate, type, name);
} }
// Initialize it if necessary. // Initialize it if necessary.
...@@ -2309,7 +2304,7 @@ RUNTIME_FUNCTION(MaybeObject*, RuntimeHidden_DeclareContextSlot) { ...@@ -2309,7 +2304,7 @@ RUNTIME_FUNCTION(MaybeObject*, RuntimeHidden_DeclareContextSlot) {
LookupResult lookup(isolate); LookupResult lookup(isolate);
object->Lookup(*name, &lookup); object->Lookup(*name, &lookup);
if (lookup.IsPropertyCallbacks()) { if (lookup.IsPropertyCallbacks()) {
return ThrowRedeclarationError(isolate, "const", name); return ThrowRedeclarationError(isolate, name);
} }
} }
if (object->IsJSGlobalObject()) { if (object->IsJSGlobalObject()) {
......
...@@ -1480,9 +1480,10 @@ TEST(ParserSync) { ...@@ -1480,9 +1480,10 @@ TEST(ParserSync) {
"break", "break",
"break label", "break label",
"break\nlabel", "break\nlabel",
"return", // TODO(marja): activate once parsing 'return' is merged into ParserBase.
"return 12", // "return",
"return\n12", // "return 12",
// "return\n12",
"with ({}) ;", "with ({}) ;",
"with ({}) {}", "with ({}) {}",
"with ({}) 12", "with ({}) 12",
......
...@@ -54,7 +54,7 @@ Debug.setListener(listener); ...@@ -54,7 +54,7 @@ Debug.setListener(listener);
var q = 42; var q = 42;
var prefixes = [ "debugger; ", var prefixes = [ "debugger; ",
"if (false) { try { throw 0; } catch(x) { return x; } }; debugger; " ]; "if (false) { try { throw 0; } catch(x) { this.x = x; } }; debugger; " ];
var bodies = [ "1", var bodies = [ "1",
"1 ", "1 ",
"1;", "1;",
......
...@@ -55,7 +55,7 @@ Debug.setListener(listener); ...@@ -55,7 +55,7 @@ Debug.setListener(listener);
var q = 42; var q = 42;
var prefixes = [ var prefixes = [
"debugger; ", "debugger; ",
"if (false) { try { throw 0; } catch(x) { return x; } }; debugger; " ]; "if (false) { try { throw 0; } catch(x) { this.x = x; } }; debugger; " ];
var with_bodies = [ "with ({}) {}", var with_bodies = [ "with ({}) {}",
"with ({x:1}) x", "with ({x:1}) x",
"with ({x:1}) x = 1", "with ({x:1}) x = 1",
......
...@@ -55,7 +55,7 @@ Debug.setListener(listener); ...@@ -55,7 +55,7 @@ Debug.setListener(listener);
var q = 42; var q = 42;
var prefixes = [ var prefixes = [
"debugger; ", "debugger; ",
"if (false) { try { throw 0; } catch(x) { return x; } }; debugger; " ]; "if (false) { try { throw 0; } catch(x) { this.x = x; } }; debugger; " ];
var bodies = [ "1", var bodies = [ "1",
"1 ", "1 ",
"1;", "1;",
......
...@@ -54,7 +54,7 @@ Debug.setListener(listener); ...@@ -54,7 +54,7 @@ Debug.setListener(listener);
var q = 42; var q = 42;
var prefixes = [ "debugger; ", var prefixes = [ "debugger; ",
"if (false) { try { throw 0; } catch(x) { return x; } }; debugger; " ]; "if (false) { try { throw 0; } catch(x) { this.x = x; } }; debugger; " ];
var with_bodies = [ "with ({}) {}", var with_bodies = [ "with ({}) {}",
"with ({x:1}) x", "with ({x:1}) x",
"with ({x:1}) x = 1", "with ({x:1}) x = 1",
......
...@@ -54,7 +54,7 @@ Debug.setListener(listener); ...@@ -54,7 +54,7 @@ Debug.setListener(listener);
var q = 42; var q = 42;
var prefixes = [ "debugger; ", var prefixes = [ "debugger; ",
"if (false) { try { throw 0; } catch(x) { return x; } }; debugger; " ]; "if (false) { try { throw 0; } catch(x) { this.x = x; } }; debugger; " ];
var bodies = [ "1", var bodies = [ "1",
"1 ", "1 ",
"1;", "1;",
......
...@@ -25,18 +25,11 @@ ...@@ -25,18 +25,11 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// To be compatible with JSC syntax errors for illegal returns should be delayed // Throw syntax errors for illegal return, break and continue at compile time.
// to runtime. assertThrows("if (false) return;");
// Invalid continue and break statements are caught at compile time.
// Do not throw syntax errors for illegal return at compile time.
assertDoesNotThrow("if (false) return;");
// Throw syntax errors for illegal break and continue at compile time.
assertThrows("if (false) break;"); assertThrows("if (false) break;");
assertThrows("if (false) continue;"); assertThrows("if (false) continue;");
// Throw syntax errors for illegal return, break and continue at runtime.
assertThrows("return;"); assertThrows("return;");
assertThrows("break;"); assertThrows("break;");
assertThrows("continue;"); assertThrows("continue;");
...@@ -157,7 +157,7 @@ PASS access_after_delete_extra_5(1, 2, 3, 4, 5) is 5 ...@@ -157,7 +157,7 @@ PASS access_after_delete_extra_5(1, 2, 3, 4, 5) is 5
PASS argumentsParam(true) is true PASS argumentsParam(true) is true
PASS argumentsFunctionConstructorParam(true) is true PASS argumentsFunctionConstructorParam(true) is true
PASS argumentsVarUndefined() is '[object Arguments]' PASS argumentsVarUndefined() is '[object Arguments]'
FAIL argumentsConstUndefined() should be [object Arguments]. Threw exception TypeError: Variable 'arguments' has already been declared FAIL argumentsConstUndefined() should be [object Arguments]. Threw exception TypeError: Identifier 'arguments' has already been declared
PASS argumentCalleeInException() is argumentCalleeInException PASS argumentCalleeInException() is argumentCalleeInException
PASS shadowedArgumentsApply([true]) is true PASS shadowedArgumentsApply([true]) is true
PASS shadowedArgumentsLength([]) is 0 PASS shadowedArgumentsLength([]) is 0
......
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