Commit c6c504f8 authored by keuchel@chromium.org's avatar keuchel@chromium.org

Parse harmony let declarations.

Implementation of the harmony block scoped let bindings as proposed here:
http://wiki.ecmascript.org/doku.php?id=harmony:block_scoped_bindings

Changes to the syntax are explained there. They are active under the
harmony_block_scoping_ flag in the parser.

Review URL: http://codereview.chromium.org/7616009

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8944 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 9295a3a0
......@@ -35,6 +35,7 @@
#include "debug.h"
#include "deoptimizer.h"
#include "execution.h"
#include "flags.h"
#include "global-handles.h"
#include "heap-profiler.h"
#include "messages.h"
......@@ -1405,7 +1406,7 @@ void ObjectTemplate::SetInternalFieldCount(int value) {
ScriptData* ScriptData::PreCompile(const char* input, int length) {
i::Utf8ToUC16CharacterStream stream(
reinterpret_cast<const unsigned char*>(input), length);
return i::ParserApi::PreParse(&stream, NULL);
return i::ParserApi::PreParse(&stream, NULL, i::FLAG_harmony_block_scoping);
}
......@@ -1414,10 +1415,10 @@ ScriptData* ScriptData::PreCompile(v8::Handle<String> source) {
if (str->IsExternalTwoByteString()) {
i::ExternalTwoByteStringUC16CharacterStream stream(
i::Handle<i::ExternalTwoByteString>::cast(str), 0, str->length());
return i::ParserApi::PreParse(&stream, NULL);
return i::ParserApi::PreParse(&stream, NULL, i::FLAG_harmony_block_scoping);
} else {
i::GenericStringUC16CharacterStream stream(str, 0, str->length());
return i::ParserApi::PreParse(&stream, NULL);
return i::ParserApi::PreParse(&stream, NULL, i::FLAG_harmony_block_scoping);
}
}
......
......@@ -742,9 +742,9 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable,
__ mov(r2, Operand(variable->name()));
// Declaration nodes are always introduced in one of two modes.
ASSERT(mode == Variable::VAR ||
mode == Variable::CONST);
PropertyAttributes attr =
(mode == Variable::VAR) ? NONE : READ_ONLY;
mode == Variable::CONST ||
mode == Variable::LET);
PropertyAttributes attr = (mode == Variable::CONST) ? READ_ONLY : NONE;
__ mov(r1, Operand(Smi::FromInt(attr)));
// Push initial value, if any.
// Note: For variables we must not push an initial value (such as
......
......@@ -375,9 +375,11 @@ class Declaration: public AstNode {
: proxy_(proxy),
mode_(mode),
fun_(fun) {
ASSERT(mode == Variable::VAR || mode == Variable::CONST);
ASSERT(mode == Variable::VAR ||
mode == Variable::CONST ||
mode == Variable::LET);
// At the moment there are no "const functions"'s in JavaScript...
ASSERT(fun == NULL || mode == Variable::VAR);
ASSERT(fun == NULL || mode == Variable::VAR || mode == Variable::LET);
}
DECLARE_NODE_TYPE(Declaration)
......
......@@ -478,15 +478,21 @@ Handle<SharedFunctionInfo> Compiler::Compile(Handle<String> source,
// that would be compiled lazily anyway, so we skip the preparse step
// in that case too.
ScriptDataImpl* pre_data = input_pre_data;
bool harmony_block_scoping = natives != NATIVES_CODE &&
FLAG_harmony_block_scoping;
if (pre_data == NULL
&& source_length >= FLAG_min_preparse_length) {
if (source->IsExternalTwoByteString()) {
ExternalTwoByteStringUC16CharacterStream stream(
Handle<ExternalTwoByteString>::cast(source), 0, source->length());
pre_data = ParserApi::PartialPreParse(&stream, extension);
pre_data = ParserApi::PartialPreParse(&stream,
extension,
harmony_block_scoping);
} else {
GenericStringUC16CharacterStream stream(source, 0, source->length());
pre_data = ParserApi::PartialPreParse(&stream, extension);
pre_data = ParserApi::PartialPreParse(&stream,
extension,
harmony_block_scoping);
}
}
......
......@@ -180,6 +180,7 @@ Handle<Object> Context::Lookup(Handle<String> name,
switch (mode) {
case Variable::INTERNAL: // Fall through.
case Variable::VAR:
case Variable::LET:
*attributes = NONE;
break;
case Variable::CONST:
......
......@@ -737,8 +737,10 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable,
__ push(esi);
__ push(Immediate(variable->name()));
// Declaration nodes are always introduced in one of two modes.
ASSERT(mode == Variable::VAR || mode == Variable::CONST);
PropertyAttributes attr = (mode == Variable::VAR) ? NONE : READ_ONLY;
ASSERT(mode == Variable::VAR ||
mode == Variable::CONST ||
mode == Variable::LET);
PropertyAttributes attr = (mode == Variable::CONST) ? READ_ONLY : NONE;
__ push(Immediate(Smi::FromInt(attr)));
// Push initial value, if any.
// Note: For variables we must not push an initial value (such as
......
......@@ -247,6 +247,7 @@ function FormatMessage(message) {
strict_cannot_assign: ["Cannot assign to read only '", "%0", "' in strict mode"],
strict_poison_pill: ["'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them"],
strict_caller: ["Illegal access to a strict mode caller function."],
unprotected_let: ["Illegal let declaration in unprotected statement context."],
};
}
var message_type = %MessageGetType(message);
......
This diff is collapsed.
......@@ -164,12 +164,14 @@ class ParserApi {
// Generic preparser generating full preparse data.
static ScriptDataImpl* PreParse(UC16CharacterStream* source,
v8::Extension* extension);
v8::Extension* extension,
bool harmony_block_scoping);
// Preparser that only does preprocessing that makes sense if only used
// immediately after.
static ScriptDataImpl* PartialPreParse(UC16CharacterStream* source,
v8::Extension* extension);
v8::Extension* extension,
bool harmony_block_scoping);
};
// ----------------------------------------------------------------------------
......@@ -452,6 +454,12 @@ class Parser {
PARSE_EAGERLY
};
enum VariableDeclarationContext {
kSourceElement,
kStatement,
kForStatement
};
Isolate* isolate() { return isolate_; }
Zone* zone() { return isolate_->zone(); }
......@@ -480,13 +488,15 @@ class Parser {
// for failure at the call sites.
void* ParseSourceElements(ZoneList<Statement*>* processor,
int end_token, bool* ok);
Statement* ParseSourceElement(ZoneStringList* labels, bool* ok);
Statement* ParseStatement(ZoneStringList* labels, bool* ok);
Statement* ParseFunctionDeclaration(bool* ok);
Statement* ParseNativeDeclaration(bool* ok);
Block* ParseBlock(ZoneStringList* labels, bool* ok);
Block* ParseScopedBlock(ZoneStringList* labels, bool* ok);
Block* ParseVariableStatement(bool* ok);
Block* ParseVariableDeclarations(bool accept_IN,
Block* ParseVariableStatement(VariableDeclarationContext var_context,
bool* ok);
Block* ParseVariableDeclarations(VariableDeclarationContext var_context,
Handle<String>* out,
bool* ok);
Statement* ParseExpressionOrLabelledStatement(ZoneStringList* labels,
......
......@@ -28,6 +28,7 @@
#include "../include/v8-preparser.h"
#include "globals.h"
#include "flags.h"
#include "checks.h"
#include "allocation.h"
#include "utils.h"
......
......@@ -112,6 +112,16 @@ void PreParser::CheckOctalLiteral(int beg_pos, int end_pos, bool* ok) {
#undef DUMMY
PreParser::Statement PreParser::ParseSourceElement(bool* ok) {
switch (peek()) {
case i::Token::LET:
return ParseVariableStatement(kSourceElement, ok);
default:
return ParseStatement(ok);
}
}
PreParser::SourceElements PreParser::ParseSourceElements(int end_token,
bool* ok) {
// SourceElements ::
......@@ -119,7 +129,7 @@ PreParser::SourceElements PreParser::ParseSourceElements(int end_token,
bool allow_directive_prologue = true;
while (peek() != end_token) {
Statement statement = ParseStatement(CHECK_OK);
Statement statement = ParseSourceElement(CHECK_OK);
if (allow_directive_prologue) {
if (statement.IsUseStrictLiteral()) {
set_strict_mode();
......@@ -172,7 +182,7 @@ PreParser::Statement PreParser::ParseStatement(bool* ok) {
case i::Token::CONST:
case i::Token::VAR:
return ParseVariableStatement(ok);
return ParseVariableStatement(kStatement, ok);
case i::Token::SEMICOLON:
Next();
......@@ -258,7 +268,7 @@ PreParser::Statement PreParser::ParseBlock(bool* ok) {
Expect(i::Token::LBRACE, CHECK_OK);
while (peek() != i::Token::RBRACE) {
i::Scanner::Location start_location = scanner_->peek_location();
Statement statement = ParseStatement(CHECK_OK);
Statement statement = ParseSourceElement(CHECK_OK);
i::Scanner::Location end_location = scanner_->location();
if (strict_mode() && statement.IsFunctionDeclaration()) {
ReportMessageAt(start_location.beg_pos, end_location.end_pos,
......@@ -272,11 +282,15 @@ PreParser::Statement PreParser::ParseBlock(bool* ok) {
}
PreParser::Statement PreParser::ParseVariableStatement(bool* ok) {
PreParser::Statement PreParser::ParseVariableStatement(
VariableDeclarationContext var_context,
bool* ok) {
// VariableStatement ::
// VariableDeclarations ';'
Statement result = ParseVariableDeclarations(true, NULL, CHECK_OK);
Statement result = ParseVariableDeclarations(var_context,
NULL,
CHECK_OK);
ExpectSemicolon(CHECK_OK);
return result;
}
......@@ -287,9 +301,10 @@ PreParser::Statement PreParser::ParseVariableStatement(bool* ok) {
// *var is untouched; in particular, it is the caller's responsibility
// to initialize it properly. This mechanism is also used for the parsing
// of 'for-in' loops.
PreParser::Statement PreParser::ParseVariableDeclarations(bool accept_IN,
int* num_decl,
bool* ok) {
PreParser::Statement PreParser::ParseVariableDeclarations(
VariableDeclarationContext var_context,
int* num_decl,
bool* ok) {
// VariableDeclarations ::
// ('var' | 'const') (Identifier ('=' AssignmentExpression)?)+[',']
......@@ -304,13 +319,25 @@ PreParser::Statement PreParser::ParseVariableDeclarations(bool accept_IN,
return Statement::Default();
}
Consume(i::Token::CONST);
} else if (peek() == i::Token::LET) {
if (var_context != kSourceElement &&
var_context != kForStatement) {
i::Scanner::Location location = scanner_->peek_location();
ReportMessageAt(location.beg_pos, location.end_pos,
"unprotected_let", NULL);
*ok = false;
return Statement::Default();
}
Consume(i::Token::LET);
} else {
*ok = false;
return Statement::Default();
}
// The scope of a variable/const declared anywhere inside a function
// is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). .
// The scope of a var/const declared variable anywhere inside a function
// is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). The scope
// of a let declared variable is the scope of the immediately enclosing
// block.
int nvars = 0; // the number of variables declared
do {
// Parse variable name.
......@@ -326,7 +353,7 @@ PreParser::Statement PreParser::ParseVariableDeclarations(bool accept_IN,
nvars++;
if (peek() == i::Token::ASSIGN) {
Expect(i::Token::ASSIGN, CHECK_OK);
ParseAssignmentExpression(accept_IN, CHECK_OK);
ParseAssignmentExpression(var_context != kForStatement, CHECK_OK);
}
} while (peek() == i::Token::COMMA);
......@@ -535,9 +562,10 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) {
Expect(i::Token::FOR, CHECK_OK);
Expect(i::Token::LPAREN, CHECK_OK);
if (peek() != i::Token::SEMICOLON) {
if (peek() == i::Token::VAR || peek() == i::Token::CONST) {
if (peek() == i::Token::VAR || peek() == i::Token::CONST ||
peek() == i::Token::LET) {
int decl_count;
ParseVariableDeclarations(false, &decl_count, CHECK_OK);
ParseVariableDeclarations(kForStatement, &decl_count, CHECK_OK);
if (peek() == i::Token::IN && decl_count == 1) {
Expect(i::Token::IN, CHECK_OK);
ParseExpression(true, CHECK_OK);
......
......@@ -77,6 +77,12 @@ class PreParser {
kFunctionScope
};
enum VariableDeclarationContext {
kSourceElement,
kStatement,
kForStatement
};
class Expression;
class Identifier {
......@@ -344,7 +350,8 @@ class PreParser {
strict_mode_violation_type_(NULL),
stack_overflow_(false),
allow_lazy_(true),
parenthesized_function_(false) { }
parenthesized_function_(false),
harmony_block_scoping_(scanner->HarmonyBlockScoping()) { }
// Preparse the program. Only called in PreParseProgram after creating
// the instance.
......@@ -377,12 +384,16 @@ class PreParser {
// which is set to false if parsing failed; it is unchanged otherwise.
// By making the 'exception handling' explicit, we are forced to check
// for failure at the call sites.
Statement ParseSourceElement(bool* ok);
SourceElements ParseSourceElements(int end_token, bool* ok);
Statement ParseStatement(bool* ok);
Statement ParseFunctionDeclaration(bool* ok);
Statement ParseBlock(bool* ok);
Statement ParseVariableStatement(bool* ok);
Statement ParseVariableDeclarations(bool accept_IN, int* num_decl, bool* ok);
Statement ParseVariableStatement(VariableDeclarationContext var_context,
bool* ok);
Statement ParseVariableDeclarations(VariableDeclarationContext var_context,
int* num_decl,
bool* ok);
Statement ParseExpressionOrLabelledStatement(bool* ok);
Statement ParseIfStatement(bool* ok);
Statement ParseContinueStatement(bool* ok);
......@@ -496,6 +507,7 @@ class PreParser {
bool stack_overflow_;
bool allow_lazy_;
bool parenthesized_function_;
bool harmony_block_scoping_;
};
} } // v8::preparser
......
......@@ -11374,6 +11374,11 @@ static Handle<Context> CopyWithContextChain(Isolate* isolate,
SerializedScopeInfo::cast(current->extension()));
new_current =
isolate->factory()->NewBlockContext(function, new_previous, scope_info);
// Copy context slots.
int num_context_slots = scope_info->NumberOfContextSlots();
for (int i = Context::MIN_CONTEXT_SLOTS; i < num_context_slots; ++i) {
new_current->set(i, current->get(i));
}
} else {
ASSERT(current->IsWithContext());
Handle<JSObject> extension(JSObject::cast(current->extension()));
......
This diff is collapsed.
......@@ -507,6 +507,14 @@ class JavaScriptScanner : public Scanner {
// tokens, which is what it is used for.
void SeekForward(int pos);
bool HarmonyBlockScoping() const {
return harmony_block_scoping_;
}
void SetHarmonyBlockScoping(bool block_scoping) {
harmony_block_scoping_ = block_scoping;
}
protected:
bool SkipWhiteSpace();
Token::Value SkipSingleLineComment();
......@@ -540,6 +548,9 @@ class JavaScriptScanner : public Scanner {
// Whether there is a multi-line comment that contains a
// line-terminator after the current token, and before the next.
bool has_multiline_comment_before_next_;
// Whether we scan 'let' as a keyword for harmony block scoped
// let bindings.
bool harmony_block_scoping_;
};
} } // namespace v8::internal
......
......@@ -397,7 +397,9 @@ Variable* Scope::DeclareLocal(Handle<String> name, Variable::Mode mode) {
// This function handles VAR and CONST modes. DYNAMIC variables are
// introduces during variable allocation, INTERNAL variables are allocated
// explicitly, and TEMPORARY variables are allocated via NewTemporary().
ASSERT(mode == Variable::VAR || mode == Variable::CONST);
ASSERT(mode == Variable::VAR ||
mode == Variable::CONST ||
mode == Variable::LET);
++num_var_or_const_;
return variables_.Declare(this, name, mode, true, Variable::NORMAL);
}
......
......@@ -168,6 +168,7 @@ namespace internal {
T(FUTURE_RESERVED_WORD, NULL, 0) \
T(FUTURE_STRICT_RESERVED_WORD, NULL, 0) \
K(CONST, "const", 0) \
K(LET, "let", 0) \
\
/* Illegal token - not able to scan. */ \
T(ILLEGAL, "ILLEGAL", 0) \
......
......@@ -41,6 +41,7 @@ const char* Variable::Mode2String(Mode mode) {
switch (mode) {
case VAR: return "VAR";
case CONST: return "CONST";
case LET: return "LET";
case DYNAMIC: return "DYNAMIC";
case DYNAMIC_GLOBAL: return "DYNAMIC_GLOBAL";
case DYNAMIC_LOCAL: return "DYNAMIC_LOCAL";
......
......@@ -46,6 +46,8 @@ class Variable: public ZoneObject {
CONST, // declared via 'const' declarations
LET, // declared via 'let' declarations
// Variables introduced by the compiler:
DYNAMIC, // always require dynamic lookup (we don't know
// the declaration)
......
......@@ -712,8 +712,10 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable,
__ push(rsi);
__ Push(variable->name());
// Declaration nodes are always introduced in one of two modes.
ASSERT(mode == Variable::VAR || mode == Variable::CONST);
PropertyAttributes attr = (mode == Variable::VAR) ? NONE : READ_ONLY;
ASSERT(mode == Variable::VAR ||
mode == Variable::CONST ||
mode == Variable::LET);
PropertyAttributes attr = (mode == Variable::CONST) ? READ_ONLY : NONE;
__ Push(Smi::FromInt(attr));
// Push initial value, if any.
// Note: For variables we must not push an initial value (such as
......
......@@ -65,6 +65,8 @@ TEST(ScanKeywords) {
i::Utf8ToUC16CharacterStream stream(keyword, length);
i::JavaScriptScanner scanner(&unicode_cache);
scanner.Initialize(&stream);
// The scanner should parse 'let' as Token::LET for this test.
scanner.SetHarmonyBlockScoping(true);
CHECK_EQ(key_token.token, scanner.Next());
CHECK_EQ(i::Token::EOS, scanner.Next());
}
......@@ -287,7 +289,7 @@ TEST(RegressChromium62639) {
i::Utf8ToUC16CharacterStream stream(reinterpret_cast<const i::byte*>(program),
static_cast<unsigned>(strlen(program)));
i::ScriptDataImpl* data =
i::ParserApi::PreParse(&stream, NULL);
i::ParserApi::PreParse(&stream, NULL, false);
CHECK(data->HasError());
delete data;
}
......@@ -311,7 +313,7 @@ TEST(Regress928) {
i::Utf8ToUC16CharacterStream stream(reinterpret_cast<const i::byte*>(program),
static_cast<unsigned>(strlen(program)));
i::ScriptDataImpl* data =
i::ParserApi::PartialPreParse(&stream, NULL);
i::ParserApi::PartialPreParse(&stream, NULL, false);
CHECK(!data->HasError());
data->Initialize();
......
// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --expose-debug-as debug --harmony-block-scoping
// The functions used for testing backtraces. They are at the top to make the
// testing of source line/column easier.
// Get the Debug object exposed from the debug context global object.
Debug = debug.Debug;
var test_name;
var listener_delegate;
var listener_called;
var exception;
var begin_test_count = 0;
var end_test_count = 0;
var break_count = 0;
// Debug event listener which delegates.
function listener(event, exec_state, event_data, data) {
try {
if (event == Debug.DebugEvent.Break) {
break_count++;
listener_called = true;
listener_delegate(exec_state);
}
} catch (e) {
exception = e;
}
}
// Add the debug event listener.
Debug.setListener(listener);
// Initialize for a new test.
function BeginTest(name) {
test_name = name;
listener_delegate = null;
listener_called = false;
exception = null;
begin_test_count++;
}
// Check result of a test.
function EndTest() {
assertTrue(listener_called, "listerner not called for " + test_name);
assertNull(exception, test_name);
end_test_count++;
}
// Check that the scope chain contains the expected types of scopes.
function CheckScopeChain(scopes, exec_state) {
assertEquals(scopes.length, exec_state.frame().scopeCount());
for (var i = 0; i < scopes.length; i++) {
var scope = exec_state.frame().scope(i);
assertTrue(scope.isScope());
assertEquals(scopes[i], scope.scopeType());
// Check the global object when hitting the global scope.
if (scopes[i] == debug.ScopeType.Global) {
// Objects don't have same class (one is "global", other is "Object",
// so just check the properties directly.
assertPropertiesEqual(this, scope.scopeObject().value());
}
}
// Get the debug command processor.
var dcp = exec_state.debugCommandProcessor("unspecified_running_state");
// Send a scopes request and check the result.
var json;
var request_json = '{"seq":0,"type":"request","command":"scopes"}';
var response_json = dcp.processDebugJSONRequest(request_json);
var response = JSON.parse(response_json);
assertEquals(scopes.length, response.body.scopes.length);
for (var i = 0; i < scopes.length; i++) {
assertEquals(i, response.body.scopes[i].index);
assertEquals(scopes[i], response.body.scopes[i].type);
if (scopes[i] == debug.ScopeType.Local ||
scopes[i] == debug.ScopeType.Closure) {
assertTrue(response.body.scopes[i].object.ref < 0);
} else {
assertTrue(response.body.scopes[i].object.ref >= 0);
}
var found = false;
for (var j = 0; j < response.refs.length && !found; j++) {
found = response.refs[j].handle == response.body.scopes[i].object.ref;
}
assertTrue(found, "Scope object " + response.body.scopes[i].object.ref + " not found");
}
}
// Check that the content of the scope is as expected. For functions just check
// that there is a function.
function CheckScopeContent(content, number, exec_state) {
var scope = exec_state.frame().scope(number);
var count = 0;
for (var p in content) {
var property_mirror = scope.scopeObject().property(p);
if (property_mirror.isUndefined()) {
print('property ' + p + ' not found in scope');
}
assertFalse(property_mirror.isUndefined(), 'property ' + p + ' not found in scope');
if (typeof(content[p]) === 'function') {
assertTrue(property_mirror.value().isFunction());
} else {
assertEquals(content[p], property_mirror.value().value(), 'property ' + p + ' has unexpected value');
}
count++;
}
// 'arguments' and might be exposed in the local and closure scope. Just
// ignore this.
var scope_size = scope.scopeObject().properties().length;
if (!scope.scopeObject().property('arguments').isUndefined()) {
scope_size--;
}
// Also ignore synthetic variable from catch block.
if (!scope.scopeObject().property('.catch-var').isUndefined()) {
scope_size--;
}
// Skip property with empty name.
if (!scope.scopeObject().property('').isUndefined()) {
scope_size--;
}
// Also ignore synthetic variable from block scopes.
if (!scope.scopeObject().property('.block').isUndefined()) {
scope_size--;
}
if (count != scope_size) {
print('Names found in scope:');
var names = scope.scopeObject().propertyNames();
for (var i = 0; i < names.length; i++) {
print(names[i]);
}
}
assertEquals(count, scope_size);
// Get the debug command processor.
var dcp = exec_state.debugCommandProcessor("unspecified_running_state");
// Send a scope request for information on a single scope and check the
// result.
var request_json = '{"seq":0,"type":"request","command":"scope","arguments":{"number":';
request_json += scope.scopeIndex();
request_json += '}}';
var response_json = dcp.processDebugJSONRequest(request_json);
var response = JSON.parse(response_json);
assertEquals(scope.scopeType(), response.body.type);
assertEquals(number, response.body.index);
if (scope.scopeType() == debug.ScopeType.Local ||
scope.scopeType() == debug.ScopeType.Closure) {
assertTrue(response.body.object.ref < 0);
} else {
assertTrue(response.body.object.ref >= 0);
}
var found = false;
for (var i = 0; i < response.refs.length && !found; i++) {
found = response.refs[i].handle == response.body.object.ref;
}
assertTrue(found, "Scope object " + response.body.object.ref + " not found");
}
// Simple closure formed by returning an inner function referering to an outer
// block local variable and an outer function's parameter. Due to VM
// optimizations parts of the actual closure is missing from the debugger
// information.
BeginTest("Closure 1");
function closure_1(a) {
var x = 2;
let y = 3;
if (true) {
let z = 4;
function f() {
debugger;
return a + x + y + z;
};
return f;
}
}
listener_delegate = function(exec_state) {
CheckScopeChain([debug.ScopeType.Local,
debug.ScopeType.Block,
debug.ScopeType.Closure,
debug.ScopeType.Global], exec_state);
CheckScopeContent({}, 0, exec_state);
CheckScopeContent({z:4}, 1, exec_state);
CheckScopeContent({a:1,x:2,y:3}, 2, exec_state);
};
closure_1(1)();
EndTest();
// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --harmony-block-scoping
// Test let declarations in various settings.
// Global
let x;
let y = 2;
// Block local
{
let y;
let x = 3;
}
assertEquals(undefined, x);
assertEquals(2,y);
if (true) {
let y;
assertEquals(undefined, y);
}
function TestLocalThrows(str, expect) {
assertThrows("(function(){" + str + "})()", expect);
}
function TestLocalDoesNotThrow(str) {
assertDoesNotThrow("(function(){" + str + "})()");
}
// Unprotected statement
TestLocalThrows("if (true) let x;", SyntaxError);
TestLocalThrows("with ({}) let x;", SyntaxError);
TestLocalThrows("do let x; while (false)", SyntaxError);
TestLocalThrows("while (false) let x;", SyntaxError);
TestLocalDoesNotThrow("if (true) var x;");
TestLocalDoesNotThrow("with ({}) var x;");
TestLocalDoesNotThrow("do var x; while (false)");
TestLocalDoesNotThrow("while (false) var x;");
......@@ -39,20 +39,178 @@ function f1() {
}
f1();
// Dynamic lookup through block scopes.
// Dynamic lookup in and through block contexts.
function f2(one) {
var x = one + 1;
// TODO(keuchel): introduce let
// let y = one + 2;
if (one == 1) {
// Parameter
let y = one + 2;
{
let z = one + 3;
assertEquals(1, eval('one'));
// Function local var variable
assertEquals(2, eval('x'));
// Function local let variable
// TODO(keuchel): introduce let
// assertEquals(3, eval('y'));
assertEquals(3, eval('y'));
assertEquals(4, eval('z'));
}
}
f2(1);
// Lookup in and through block contexts.
function f3(one) {
var x = one + 1;
let y = one + 2;
{
let z = one + 3;
assertEquals(1, one);
assertEquals(2, x);
assertEquals(3, y);
assertEquals(4, z);
}
}
f3(1);
// Dynamic lookup from closure.
function f4(one) {
var x = one + 1;
let y = one + 2;
{
let z = one + 3;
function f() {
assertEquals(1, eval('one'));
assertEquals(2, eval('x'));
assertEquals(3, eval('y'));
assertEquals(4, eval('z'));
};
}
}
f4(1);
// Lookup from closure.
function f5(one) {
var x = one + 1;
let y = one + 2;
{
let z = one + 3;
function f() {
assertEquals(1, one);
assertEquals(2, x);
assertEquals(3, y);
assertEquals(4, z);
};
}
}
f5(1);
// Return from block.
function f6() {
let x = 1;
{
let y = 2;
return x + y;
}
}
assertEquals(3, f6(6));
// Variable shadowing and lookup.
function f7(a) {
let b = 1;
var c = 1;
var d = 1;
{ // let variables shadowing argument, let and var variables
let a = 2;
let b = 2;
let c = 2;
assertEquals(2,a);
assertEquals(2,b);
assertEquals(2,c);
}
try {
throw 'stuff1';
} catch (a) {
assertEquals('stuff1',a);
// catch variable shadowing argument
a = 2;
assertEquals(2,a);
{
// let variable shadowing catch variable
let a = 3;
assertEquals(3,a);
try {
throw 'stuff2';
} catch (a) {
assertEquals('stuff2',a);
// catch variable shadowing let variable
a = 4;
assertEquals(4,a);
}
assertEquals(3,a);
}
assertEquals(2,a);
}
try {
throw 'stuff3';
} catch (c) {
// catch variable shadowing var variable
assertEquals('stuff3',c);
try {
throw 'stuff4';
} catch(c) {
assertEquals('stuff4',c);
// catch variable shadowing catch variable
c = 3;
assertEquals(3,c);
}
(function(c) {
// argument shadowing catch variable
c = 3;
assertEquals(3,c);
})();
assertEquals('stuff3', c);
(function() {
// var variable shadowing catch variable
var c = 3;
})();
assertEquals('stuff3', c);
c = 2;
}
assertEquals(1,c);
(function(a,b,c) {
// arguments shadowing argument, let and var variable
a = 2;
b = 2;
c = 2;
assertEquals(2,a);
assertEquals(2,b);
assertEquals(2,c);
// var variable shadowing var variable
var d = 2;
})(1,1);
assertEquals(1,a);
assertEquals(1,b);
assertEquals(1,c);
assertEquals(1,d);
}
f7(1);
// Ensure let variables are block local and var variables function local.
function f8() {
var let_accessors = [];
var var_accessors = [];
for (var i = 0; i < 10; i++) {
let x = i;
var y = i;
let_accessors[i] = function() { return x; }
var_accessors[i] = function() { return y; }
}
for (var j = 0; j < 10; j++) {
y = j + 10;
assertEquals(j, let_accessors[j]());
assertEquals(y, var_accessors[j]());
}
}
f8();
......@@ -157,7 +157,6 @@ function CheckScopeContent(content, number, exec_state) {
scope_size--;
}
// TODO(keuchel): print('count' + count + ' scopesize' + scope_size);
if (count != scope_size) {
print('Names found in scope:');
var names = scope.scopeObject().propertyNames();
......@@ -236,7 +235,7 @@ EndTest();
BeginTest("Local 3");
function local_3(a) {
var x = 3;
let x = 3;
debugger;
}
......@@ -253,8 +252,8 @@ EndTest();
BeginTest("Local 4");
function local_4(a, b) {
var x = 3;
var y = 4;
let x = 3;
let y = 4;
debugger;
}
......@@ -361,29 +360,30 @@ with_block_4();
EndTest();
// TODO(keuchel):
// Simple closure formed by returning an inner function referering to an outer
// block local variable and an outer function's parameter.
// BeginTest("Closure 1");
//
// function closure_1(a) {
// {
// let x = 3;
// function f() {
// debugger;
// return a + x;
// };
// }
// return f;
// }
//
// listener_delegate = function(exec_state) {
// CheckScopeChain([debug.ScopeType.Local,
// debug.ScopeType.Closure,
// debug.ScopeType.Global], exec_state);
// // CheckScopeContent({}, 0, exec_state);
// // CheckScopeContent({}, 1, exec_state);
// // CheckScopeContent({a:1}, 2, exec_state);
// };
// closure_1(1)();
// EndTest();
BeginTest("Closure 1");
function closure_1(a) {
var x = 2;
let y = 3;
if (true) {
let z = 4;
function f() {
debugger;
return a + x + y + z;
};
return f;
}
}
listener_delegate = function(exec_state) {
CheckScopeChain([debug.ScopeType.Local,
debug.ScopeType.Block,
debug.ScopeType.Closure,
debug.ScopeType.Global], exec_state);
CheckScopeContent({}, 0, exec_state);
CheckScopeContent({a:1,x:2,y:3}, 2, exec_state);
};
closure_1(1)();
EndTest();
......@@ -25,14 +25,14 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --expose-debug-as debug
// Flags: --expose-debug-as debug --harmony-block-scoping
// Test debug evaluation for functions without local context, but with
// nested catch contexts.
function f() {
{ // Line 1.
var i = 1; // Line 2. // TODO(keuchel): introduce let
let i = 1; // Line 2.
try { // Line 3.
throw 'stuff'; // Line 4.
} catch (e) { // Line 5.
......
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