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
This commit is contained in:
rossberg@chromium.org 2014-04-02 12:38:01 +00:00
parent 511edabed2
commit 2fda95eb80
14 changed files with 48 additions and 72 deletions

View File

@ -47,7 +47,8 @@ var kMessages = {
incompatible_method_receiver: ["Method ", "%0", " called on incompatible receiver ", "%1"],
multiple_defaults_in_switch: ["More than one default clause in switch statement"],
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"],
unknown_label: ["Undefined label '", "%0", "'"],
uncaught_exception: ["Uncaught ", "%0"],

View File

@ -590,8 +590,7 @@ Expression* ParserTraits::BuildUnaryExpression(
}
Expression* ParserTraits::NewThrowReferenceError(
const char* message, int pos) {
Expression* ParserTraits::NewThrowReferenceError(const char* message, int pos) {
return NewThrowError(
parser_->isolate()->factory()->MakeReferenceError_string(),
message, HandleVector<Object>(NULL, 0), pos);
@ -609,11 +608,9 @@ Expression* ParserTraits::NewThrowSyntaxError(
Expression* ParserTraits::NewThrowTypeError(
const char* message, Handle<Object> arg1, Handle<Object> arg2, int pos) {
ASSERT(!arg1.is_null() && !arg2.is_null());
Handle<Object> elements[] = { arg1, arg2 };
Vector< Handle<Object> > arguments =
HandleVector<Object>(elements, ARRAY_SIZE(elements));
const char* message, Handle<Object> arg, int pos) {
int argc = arg.is_null() ? 0 : 1;
Vector< Handle<Object> > arguments = HandleVector<Object>(&arg, argc);
return NewThrowError(
parser_->isolate()->factory()->MakeTypeError_string(),
message, arguments, pos);
@ -1754,18 +1751,14 @@ void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) {
// In harmony we treat re-declarations as early errors. See
// ES5 16 for a definition of early errors.
SmartArrayPointer<char> c_string = name->ToCString(DISALLOW_NULLS);
const char* elms[2] = { "Variable", c_string.get() };
Vector<const char*> args(elms, 2);
ReportMessage("redeclaration", args);
const char* elms[1] = { c_string.get() };
Vector<const char*> args(elms, 1);
ReportMessage("var_redeclaration", args);
*ok = false;
return;
}
Handle<String> message_string =
isolate()->factory()->InternalizeOneByteString(
STATIC_ASCII_VECTOR("Variable"));
Expression* expression =
NewThrowTypeError("redeclaration",
message_string, name, declaration->position());
Expression* expression = NewThrowTypeError(
"var_redeclaration", name, declaration->position());
declaration_scope->SetIllegalRedeclaration(expression);
}
}
@ -2374,9 +2367,9 @@ Statement* Parser::ParseExpressionOrLabelledStatement(ZoneStringList* labels,
// make later anyway so we should go back and fix this then.
if (ContainsLabel(labels, label) || TargetStackContainsLabel(label)) {
SmartArrayPointer<char> c_string = label->ToCString(DISALLOW_NULLS);
const char* elms[2] = { "Label", c_string.get() };
Vector<const char*> args(elms, 2);
ReportMessage("redeclaration", args);
const char* elms[1] = { c_string.get() };
Vector<const char*> args(elms, 1);
ReportMessage("label_redeclaration", args);
*ok = false;
return NULL;
}
@ -2521,7 +2514,7 @@ Statement* Parser::ParseReturnStatement(bool* ok) {
// reporting any errors on it, because of the way errors are
// reported (underlining).
Expect(Token::RETURN, CHECK_OK);
int pos = position();
Scanner::Location loc = scanner()->location();
Token::Value tok = peek();
Statement* result;
@ -2539,23 +2532,17 @@ Statement* Parser::ParseReturnStatement(bool* ok) {
Expression* generator = factory()->NewVariableProxy(
function_state_->generator_object_variable());
Expression* yield = factory()->NewYield(
generator, return_value, Yield::FINAL, pos);
result = factory()->NewExpressionStatement(yield, pos);
generator, return_value, Yield::FINAL, loc.beg_pos);
result = factory()->NewExpressionStatement(yield, loc.beg_pos);
} else {
result = factory()->NewReturnStatement(return_value, pos);
result = factory()->NewReturnStatement(return_value, loc.beg_pos);
}
// An ECMAScript program is considered syntactically incorrect if it
// contains a return statement that is not within the body of a
// function. See ECMA-262, section 12.9, page 67.
//
// To be consistent with KJS we report the syntax error at runtime.
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);
Scope* decl_scope = scope_->DeclarationScope();
if (decl_scope->is_global_scope() || decl_scope->is_eval_scope()) {
ReportMessageAt(loc, "illegal_return");
*ok = false;
return NULL;
}
return result;
}
@ -3669,13 +3656,13 @@ void Parser::CheckConflictingVarDeclarations(Scope* scope, bool* ok) {
// errors. See ES5 16 for a definition of early errors.
Handle<String> name = decl->proxy()->name();
SmartArrayPointer<char> c_string = name->ToCString(DISALLOW_NULLS);
const char* elms[2] = { "Variable", c_string.get() };
Vector<const char*> args(elms, 2);
const char* elms[1] = { c_string.get() };
Vector<const char*> args(elms, 1);
int position = decl->proxy()->position();
Scanner::Location location = position == RelocInfo::kNoPosition
? Scanner::Location::invalid()
: Scanner::Location(position, position + 1);
ParserTraits::ReportMessageAt(location, "redeclaration", args);
ParserTraits::ReportMessageAt(location, "var_redeclaration", args);
*ok = false;
}
}

View File

@ -538,8 +538,7 @@ class ParserTraits {
// Generate AST node that throws a TypeError with the given
// type. Both arguments must be non-null (in the handle sense).
Expression* NewThrowTypeError(
const char* type, Handle<Object> arg1, Handle<Object> arg2, int pos);
Expression* NewThrowTypeError(const char* type, Handle<Object> arg, int pos);
// Generic AST generator for throwing errors from compiled code.
Expression* NewThrowError(

View File

@ -582,7 +582,7 @@ PreParser::Statement PreParser::ParseReturnStatement(bool* ok) {
// ReturnStatement ::
// '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
// reported (underlining).
Expect(Token::RETURN, CHECK_OK);

View File

@ -922,7 +922,7 @@ class PreParserTraits {
return PreParserExpression::Default();
}
PreParserExpression NewThrowTypeError(
const char* type, Handle<Object> arg1, Handle<Object> arg2, int pos) {
const char* type, Handle<Object> arg, int pos) {
return PreParserExpression::Default();
}

View File

@ -2117,15 +2117,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetAccessorProperty) {
}
static Failure* ThrowRedeclarationError(Isolate* isolate,
const char* type,
Handle<String> name) {
static Failure* ThrowRedeclarationError(Isolate* isolate, Handle<String> name) {
HandleScope scope(isolate);
Handle<Object> type_handle =
isolate->factory()->NewStringFromAscii(CStrVector(type));
Handle<Object> args[2] = { type_handle, name };
Handle<Object> error =
isolate->factory()->NewTypeError("redeclaration", HandleVector(args, 2));
Handle<Object> args[1] = { name };
Handle<Object> error = isolate->factory()->NewTypeError(
"var_redeclaration", HandleVector(args, 1));
return isolate->Throw(*error);
}
@ -2202,7 +2198,7 @@ RUNTIME_FUNCTION(MaybeObject*, RuntimeHidden_DeclareGlobals) {
if (lookup.IsFound() && lookup.IsDontDelete()) {
if (lookup.IsReadOnly() || lookup.IsDontEnum() ||
lookup.IsPropertyCallbacks()) {
return ThrowRedeclarationError(isolate, "function", name);
return ThrowRedeclarationError(isolate, name);
}
// If the existing property is not configurable, keep its attributes.
attr = lookup.GetAttributes();
@ -2254,8 +2250,7 @@ RUNTIME_FUNCTION(MaybeObject*, RuntimeHidden_DeclareContextSlot) {
if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
// Functions are not read-only.
ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
return ThrowRedeclarationError(isolate, type, name);
return ThrowRedeclarationError(isolate, name);
}
// Initialize it if necessary.
@ -2309,7 +2304,7 @@ RUNTIME_FUNCTION(MaybeObject*, RuntimeHidden_DeclareContextSlot) {
LookupResult lookup(isolate);
object->Lookup(*name, &lookup);
if (lookup.IsPropertyCallbacks()) {
return ThrowRedeclarationError(isolate, "const", name);
return ThrowRedeclarationError(isolate, name);
}
}
if (object->IsJSGlobalObject()) {

View File

@ -1480,9 +1480,10 @@ TEST(ParserSync) {
"break",
"break label",
"break\nlabel",
"return",
"return 12",
"return\n12",
// TODO(marja): activate once parsing 'return' is merged into ParserBase.
// "return",
// "return 12",
// "return\n12",
"with ({}) ;",
"with ({}) {}",
"with ({}) 12",

View File

@ -54,7 +54,7 @@ Debug.setListener(listener);
var q = 42;
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",
"1 ",
"1;",

View File

@ -55,7 +55,7 @@ Debug.setListener(listener);
var q = 42;
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 ({}) {}",
"with ({x:1}) x",
"with ({x:1}) x = 1",

View File

@ -55,7 +55,7 @@ Debug.setListener(listener);
var q = 42;
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",
"1 ",
"1;",

View File

@ -54,7 +54,7 @@ Debug.setListener(listener);
var q = 42;
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 ({}) {}",
"with ({x:1}) x",
"with ({x:1}) x = 1",

View File

@ -54,7 +54,7 @@ Debug.setListener(listener);
var q = 42;
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",
"1 ",
"1;",

View File

@ -25,18 +25,11 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// 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
// to runtime.
// 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.
// Throw syntax errors for illegal return, break and continue at compile time.
assertThrows("if (false) return;");
assertThrows("if (false) break;");
assertThrows("if (false) continue;");
// Throw syntax errors for illegal return, break and continue at runtime.
assertThrows("return;");
assertThrows("break;");
assertThrows("continue;");

View File

@ -157,7 +157,7 @@ PASS access_after_delete_extra_5(1, 2, 3, 4, 5) is 5
PASS argumentsParam(true) is true
PASS argumentsFunctionConstructorParam(true) is true
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 shadowedArgumentsApply([true]) is true
PASS shadowedArgumentsLength([]) is 0