#include "debug.h"
#include "deoptimizer.h"
#include "execution.h"
+#include "flags.h"
#include "global-handles.h"
#include "heap-profiler.h"
#include "messages.h"
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);
}
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);
}
}
__ 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
: 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)
// 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);
}
}
switch (mode) {
case Variable::INTERNAL: // Fall through.
case Variable::VAR:
+ case Variable::LET:
*attributes = NONE;
break;
case Variable::CONST:
__ 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
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);
}
void Parser::SetHarmonyBlockScoping(bool block_scoping) {
+ scanner().SetHarmonyBlockScoping(block_scoping);
harmony_block_scoping_ = block_scoping;
}
};
+Statement* Parser::ParseSourceElement(ZoneStringList* labels,
+ bool* ok) {
+ if (peek() == Token::FUNCTION) {
+ // FunctionDeclaration is only allowed in the context of SourceElements
+ // (Ecma 262 5th Edition, clause 14):
+ // SourceElement:
+ // Statement
+ // FunctionDeclaration
+ // Common language extension is to allow function declaration in place
+ // of any statement. This language extension is disabled in strict mode.
+ return ParseFunctionDeclaration(ok);
+ } else if (peek() == Token::LET) {
+ return ParseVariableStatement(kSourceElement, ok);
+ } else {
+ return ParseStatement(labels, ok);
+ }
+}
+
+
void* Parser::ParseSourceElements(ZoneList<Statement*>* processor,
int end_token,
bool* ok) {
}
Scanner::Location token_loc = scanner().peek_location();
-
- Statement* stat;
- if (peek() == Token::FUNCTION) {
- // FunctionDeclaration is only allowed in the context of SourceElements
- // (Ecma 262 5th Edition, clause 14):
- // SourceElement:
- // Statement
- // FunctionDeclaration
- // Common language extension is to allow function declaration in place
- // of any statement. This language extension is disabled in strict mode.
- stat = ParseFunctionDeclaration(CHECK_OK);
- } else {
- stat = ParseStatement(NULL, CHECK_OK);
- }
-
+ Statement* stat = ParseSourceElement(NULL, CHECK_OK);
if (stat == NULL || stat->IsEmpty()) {
directive_prologue = false; // End of directive prologue.
continue;
case Token::CONST: // fall through
case Token::VAR:
- stmt = ParseVariableStatement(ok);
+ stmt = ParseVariableStatement(kStatement, ok);
break;
case Token::SEMICOLON:
bool resolve,
bool* ok) {
Variable* var = NULL;
- // If we are inside a function, a declaration of a variable
- // is a truly local variable, and the scope of the variable
- // is always the function scope.
+ // If we are inside a function, a declaration of a var/const variable is a
+ // truly local variable, and the scope of the variable is always the function
+ // scope.
// If a function scope exists, then we can statically declare this
// variable and also set its mode. In any case, a Declaration node
// to the calling function context.
// Similarly, strict mode eval scope does not leak variable declarations to
// the caller's scope so we declare all locals, too.
- Scope* declaration_scope = top_scope_->DeclarationScope();
+
+ Scope* declaration_scope = mode == Variable::LET ? top_scope_
+ : top_scope_->DeclarationScope();
if (declaration_scope->is_function_scope() ||
- declaration_scope->is_strict_mode_eval_scope()) {
+ declaration_scope->is_strict_mode_eval_scope() ||
+ declaration_scope->is_block_scope()) {
// Declare the variable in the function scope.
var = declaration_scope->LocalLookup(name);
if (var == NULL) {
// Declare the name.
var = declaration_scope->DeclareLocal(name, mode);
} else {
- // The name was declared before; check for conflicting
- // re-declarations. If the previous declaration was a const or the
- // current declaration is a const then we have a conflict. There is
- // similar code in runtime.cc in the Declare functions.
- if ((mode == Variable::CONST) || (var->mode() == Variable::CONST)) {
- // We only have vars and consts in declarations.
+ // The name was declared before; check for conflicting re-declarations.
+ // We have a conflict if either of the declarations is not a var. There
+ // is similar code in runtime.cc in the Declare functions.
+ if ((mode != Variable::VAR) || (var->mode() != Variable::VAR)) {
+ // We only have vars, consts and lets in declarations.
ASSERT(var->mode() == Variable::VAR ||
- var->mode() == Variable::CONST);
- const char* type = (var->mode() == Variable::VAR) ? "var" : "const";
+ var->mode() == Variable::CONST ||
+ var->mode() == Variable::LET);
+ const char* type = (var->mode() == Variable::VAR) ? "var" :
+ (var->mode() == Variable::CONST) ? "const" : "let";
Handle<String> type_string =
isolate()->factory()->NewStringFromUtf8(CStrVector(type), TENURED);
Expression* expression =
// Even if we're not at the top-level of the global or a function
// scope, we treat is as such and introduce the function with it's
// initial value upon entering the corresponding scope.
- Declare(name, Variable::VAR, fun, true, CHECK_OK);
+ Variable::Mode mode = harmony_block_scoping_ ? Variable::LET : Variable::VAR;
+ Declare(name, mode, fun, true, CHECK_OK);
return EmptyStatement();
}
InitializationBlockFinder block_finder(top_scope_, target_stack_);
while (peek() != Token::RBRACE) {
- Statement* stat = ParseStatement(NULL, CHECK_OK);
+ Statement* stat = ParseSourceElement(NULL, CHECK_OK);
if (stat && !stat->IsEmpty()) {
body->AddStatement(stat);
block_finder.Update(stat);
}
-Block* Parser::ParseVariableStatement(bool* ok) {
+Block* Parser::ParseVariableStatement(VariableDeclarationContext var_context,
+ bool* ok) {
// VariableStatement ::
// VariableDeclarations ';'
Handle<String> ignore;
- Block* result = ParseVariableDeclarations(true, &ignore, CHECK_OK);
+ Block* result = ParseVariableDeclarations(var_context,
+ &ignore,
+ CHECK_OK);
ExpectSemicolon(CHECK_OK);
return result;
}
// *var is untouched; in particular, it is the caller's responsibility
// to initialize it properly. This mechanism is used for the parsing
// of 'for-in' loops.
-Block* Parser::ParseVariableDeclarations(bool accept_IN,
+Block* Parser::ParseVariableDeclarations(VariableDeclarationContext var_context,
Handle<String>* out,
bool* ok) {
// VariableDeclarations ::
Variable::Mode mode = Variable::VAR;
bool is_const = false;
- Scope* declaration_scope = top_scope_->DeclarationScope();
if (peek() == Token::VAR) {
Consume(Token::VAR);
} else if (peek() == Token::CONST) {
Consume(Token::CONST);
- if (declaration_scope->is_strict_mode()) {
+ if (top_scope_->is_strict_mode()) {
ReportMessage("strict_const", Vector<const char*>::empty());
*ok = false;
return NULL;
}
mode = Variable::CONST;
is_const = true;
+ } else if (peek() == Token::LET) {
+ Consume(Token::LET);
+ if (var_context != kSourceElement &&
+ var_context != kForStatement) {
+ ASSERT(var_context == kStatement);
+ ReportMessage("unprotected_let", Vector<const char*>::empty());
+ *ok = false;
+ return NULL;
+ }
+ mode = Variable::LET;
} else {
UNREACHABLE(); // by current callers
}
- // The scope of a variable/const declared anywhere inside a function
+ Scope* declaration_scope = mode == Variable::LET
+ ? top_scope_ : top_scope_->DeclarationScope();
+ // 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). Thus we can
- // transform a source-level variable/const declaration into a (Function)
+ // transform a source-level var/const declaration into a (Function)
// Scope declaration, and rewrite the source-level initialization into an
// assignment statement. We use a block to collect multiple assignments.
//
if (peek() == Token::ASSIGN) {
Expect(Token::ASSIGN, CHECK_OK);
position = scanner().location().beg_pos;
- value = ParseAssignmentExpression(accept_IN, CHECK_OK);
+ value = ParseAssignmentExpression(var_context != kForStatement, CHECK_OK);
// Don't infer if it is "a = function(){...}();"-like expression.
if (fni_ != NULL &&
value->AsCall() == NULL &&
if (peek() == Token::VAR || peek() == Token::CONST) {
Handle<String> name;
Block* variable_statement =
- ParseVariableDeclarations(false, &name, CHECK_OK);
+ ParseVariableDeclarations(kForStatement, &name, CHECK_OK);
if (peek() == Token::IN && !name.is_null()) {
VariableProxy* each = top_scope_->NewUnresolved(name, inside_with());
}
int num_parameters = 0;
- // Function declarations are hoisted.
- Scope* scope = (type == FunctionLiteral::DECLARATION)
+ // Function declarations are function scoped in normal mode, so they are
+ // hoisted. In harmony block scoping mode they are block scoped, so they
+ // are not hoisted.
+ Scope* scope = (type == FunctionLiteral::DECLARATION &&
+ !harmony_block_scoping_)
? NewScope(top_scope_->DeclarationScope(), Scope::FUNCTION_SCOPE, false)
: NewScope(top_scope_, Scope::FUNCTION_SCOPE, inside_with());
ZoneList<Statement*>* body = new(zone()) ZoneList<Statement*>(8);
}
-// Parses and identifier that is valid for the current scope, in particular it
+// Parses an identifier that is valid for the current scope, in particular it
// fails on strict mode future reserved keywords in a strict scope.
Handle<String> Parser::ParseIdentifier(bool* ok) {
if (top_scope_->is_strict_mode()) {
// Create a Scanner for the preparser to use as input, and preparse the source.
static ScriptDataImpl* DoPreParse(UC16CharacterStream* source,
bool allow_lazy,
- ParserRecorder* recorder) {
+ ParserRecorder* recorder,
+ bool harmony_block_scoping) {
Isolate* isolate = Isolate::Current();
JavaScriptScanner scanner(isolate->unicode_cache());
+ scanner.SetHarmonyBlockScoping(harmony_block_scoping);
scanner.Initialize(source);
intptr_t stack_limit = isolate->stack_guard()->real_climit();
if (!preparser::PreParser::PreParseProgram(&scanner,
// Preparse, but only collect data that is immediately useful,
// even if the preparser data is only used once.
ScriptDataImpl* ParserApi::PartialPreParse(UC16CharacterStream* source,
- v8::Extension* extension) {
+ v8::Extension* extension,
+ bool harmony_block_scoping) {
bool allow_lazy = FLAG_lazy && (extension == NULL);
if (!allow_lazy) {
// Partial preparsing is only about lazily compiled functions.
return NULL;
}
PartialParserRecorder recorder;
- return DoPreParse(source, allow_lazy, &recorder);
+ return DoPreParse(source, allow_lazy, &recorder, harmony_block_scoping);
}
ScriptDataImpl* ParserApi::PreParse(UC16CharacterStream* source,
- v8::Extension* extension) {
+ v8::Extension* extension,
+ bool harmony_block_scoping) {
Handle<Script> no_script;
bool allow_lazy = FLAG_lazy && (extension == NULL);
CompleteParserRecorder recorder;
- return DoPreParse(source, allow_lazy, &recorder);
+ return DoPreParse(source, allow_lazy, &recorder, harmony_block_scoping);
}
ASSERT(info->function() == NULL);
FunctionLiteral* result = NULL;
Handle<Script> script = info->script();
+ bool harmony_block_scoping = !info->is_native() &&
+ FLAG_harmony_block_scoping;
if (info->is_lazy()) {
Parser parser(script, true, NULL, NULL);
- parser.SetHarmonyBlockScoping(!info->is_native() &&
- FLAG_harmony_block_scoping);
+ parser.SetHarmonyBlockScoping(harmony_block_scoping);
result = parser.ParseLazy(info);
} else {
// Whether we allow %identifier(..) syntax.
bool allow_natives_syntax =
info->allows_natives_syntax() || FLAG_allow_natives_syntax;
ScriptDataImpl* pre_data = info->pre_parse_data();
- Parser parser(script, allow_natives_syntax, info->extension(), pre_data);
- parser.SetHarmonyBlockScoping(!info->is_native() &&
- FLAG_harmony_block_scoping);
+ Parser parser(script,
+ allow_natives_syntax,
+ info->extension(),
+ pre_data);
+ parser.SetHarmonyBlockScoping(harmony_block_scoping);
if (pre_data != NULL && pre_data->has_error()) {
Scanner::Location loc = pre_data->MessageLocation();
const char* message = pre_data->BuildMessage();
// 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);
};
// ----------------------------------------------------------------------------
PARSE_EAGERLY
};
+ enum VariableDeclarationContext {
+ kSourceElement,
+ kStatement,
+ kForStatement
+ };
+
Isolate* isolate() { return isolate_; }
Zone* zone() { return isolate_->zone(); }
// 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,
#include "../include/v8-preparser.h"
#include "globals.h"
+#include "flags.h"
#include "checks.h"
#include "allocation.h"
#include "utils.h"
#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 ::
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();
case i::Token::CONST:
case i::Token::VAR:
- return ParseVariableStatement(ok);
+ return ParseVariableStatement(kStatement, ok);
case i::Token::SEMICOLON:
Next();
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,
}
-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;
}
// *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)?)+[',']
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.
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);
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);
kFunctionScope
};
+ enum VariableDeclarationContext {
+ kSourceElement,
+ kStatement,
+ kForStatement
+ };
+
class Expression;
class Identifier {
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.
// 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);
bool stack_overflow_;
bool allow_lazy_;
bool parenthesized_function_;
+ bool harmony_block_scoping_;
};
} } // v8::preparser
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()));
// JavaScriptScanner
JavaScriptScanner::JavaScriptScanner(UnicodeCache* scanner_contants)
- : Scanner(scanner_contants), octal_pos_(Location::invalid()) { }
+ : Scanner(scanner_contants),
+ octal_pos_(Location::invalid()) { }
void JavaScriptScanner::Initialize(UC16CharacterStream* source) {
// ----------------------------------------------------------------------------
// Keyword Matcher
-#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \
- KEYWORD_GROUP('b') \
- KEYWORD("break", BREAK) \
- KEYWORD_GROUP('c') \
- KEYWORD("case", CASE) \
- KEYWORD("catch", CATCH) \
- KEYWORD("class", FUTURE_RESERVED_WORD) \
- KEYWORD("const", CONST) \
- KEYWORD("continue", CONTINUE) \
- KEYWORD_GROUP('d') \
- KEYWORD("debugger", DEBUGGER) \
- KEYWORD("default", DEFAULT) \
- KEYWORD("delete", DELETE) \
- KEYWORD("do", DO) \
- KEYWORD_GROUP('e') \
- KEYWORD("else", ELSE) \
- KEYWORD("enum", FUTURE_RESERVED_WORD) \
- KEYWORD("export", FUTURE_RESERVED_WORD) \
- KEYWORD("extends", FUTURE_RESERVED_WORD) \
- KEYWORD_GROUP('f') \
- KEYWORD("false", FALSE_LITERAL) \
- KEYWORD("finally", FINALLY) \
- KEYWORD("for", FOR) \
- KEYWORD("function", FUNCTION) \
- KEYWORD_GROUP('i') \
- KEYWORD("if", IF) \
- KEYWORD("implements", FUTURE_STRICT_RESERVED_WORD) \
- KEYWORD("import", FUTURE_RESERVED_WORD) \
- KEYWORD("in", IN) \
- KEYWORD("instanceof", INSTANCEOF) \
- KEYWORD("interface", FUTURE_STRICT_RESERVED_WORD) \
- KEYWORD_GROUP('l') \
- KEYWORD("let", FUTURE_STRICT_RESERVED_WORD) \
- KEYWORD_GROUP('n') \
- KEYWORD("new", NEW) \
- KEYWORD("null", NULL_LITERAL) \
- KEYWORD_GROUP('p') \
- KEYWORD("package", FUTURE_STRICT_RESERVED_WORD) \
- KEYWORD("private", FUTURE_STRICT_RESERVED_WORD) \
- KEYWORD("protected", FUTURE_STRICT_RESERVED_WORD) \
- KEYWORD("public", FUTURE_STRICT_RESERVED_WORD) \
- KEYWORD_GROUP('r') \
- KEYWORD("return", RETURN) \
- KEYWORD_GROUP('s') \
- KEYWORD("static", FUTURE_STRICT_RESERVED_WORD) \
- KEYWORD("super", FUTURE_RESERVED_WORD) \
- KEYWORD("switch", SWITCH) \
- KEYWORD_GROUP('t') \
- KEYWORD("this", THIS) \
- KEYWORD("throw", THROW) \
- KEYWORD("true", TRUE_LITERAL) \
- KEYWORD("try", TRY) \
- KEYWORD("typeof", TYPEOF) \
- KEYWORD_GROUP('v') \
- KEYWORD("var", VAR) \
- KEYWORD("void", VOID) \
- KEYWORD_GROUP('w') \
- KEYWORD("while", WHILE) \
- KEYWORD("with", WITH) \
- KEYWORD_GROUP('y') \
- KEYWORD("yield", FUTURE_STRICT_RESERVED_WORD)
+#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \
+ KEYWORD_GROUP('b') \
+ KEYWORD("break", Token::BREAK) \
+ KEYWORD_GROUP('c') \
+ KEYWORD("case", Token::CASE) \
+ KEYWORD("catch", Token::CATCH) \
+ KEYWORD("class", Token::FUTURE_RESERVED_WORD) \
+ KEYWORD("const", Token::CONST) \
+ KEYWORD("continue", Token::CONTINUE) \
+ KEYWORD_GROUP('d') \
+ KEYWORD("debugger", Token::DEBUGGER) \
+ KEYWORD("default", Token::DEFAULT) \
+ KEYWORD("delete", Token::DELETE) \
+ KEYWORD("do", Token::DO) \
+ KEYWORD_GROUP('e') \
+ KEYWORD("else", Token::ELSE) \
+ KEYWORD("enum", Token::FUTURE_RESERVED_WORD) \
+ KEYWORD("export", Token::FUTURE_RESERVED_WORD) \
+ KEYWORD("extends", Token::FUTURE_RESERVED_WORD) \
+ KEYWORD_GROUP('f') \
+ KEYWORD("false", Token::FALSE_LITERAL) \
+ KEYWORD("finally", Token::FINALLY) \
+ KEYWORD("for", Token::FOR) \
+ KEYWORD("function", Token::FUNCTION) \
+ KEYWORD_GROUP('i') \
+ KEYWORD("if", Token::IF) \
+ KEYWORD("implements", Token::FUTURE_STRICT_RESERVED_WORD) \
+ KEYWORD("import", Token::FUTURE_RESERVED_WORD) \
+ KEYWORD("in", Token::IN) \
+ KEYWORD("instanceof", Token::INSTANCEOF) \
+ KEYWORD("interface", Token::FUTURE_STRICT_RESERVED_WORD) \
+ KEYWORD_GROUP('l') \
+ KEYWORD("let", harmony_block_scoping \
+ ? Token::LET : Token::FUTURE_STRICT_RESERVED_WORD) \
+ KEYWORD_GROUP('n') \
+ KEYWORD("new", Token::NEW) \
+ KEYWORD("null", Token::NULL_LITERAL) \
+ KEYWORD_GROUP('p') \
+ KEYWORD("package", Token::FUTURE_STRICT_RESERVED_WORD) \
+ KEYWORD("private", Token::FUTURE_STRICT_RESERVED_WORD) \
+ KEYWORD("protected", Token::FUTURE_STRICT_RESERVED_WORD) \
+ KEYWORD("public", Token::FUTURE_STRICT_RESERVED_WORD) \
+ KEYWORD_GROUP('r') \
+ KEYWORD("return", Token::RETURN) \
+ KEYWORD_GROUP('s') \
+ KEYWORD("static", Token::FUTURE_STRICT_RESERVED_WORD) \
+ KEYWORD("super", Token::FUTURE_RESERVED_WORD) \
+ KEYWORD("switch", Token::SWITCH) \
+ KEYWORD_GROUP('t') \
+ KEYWORD("this", Token::THIS) \
+ KEYWORD("throw", Token::THROW) \
+ KEYWORD("true", Token::TRUE_LITERAL) \
+ KEYWORD("try", Token::TRY) \
+ KEYWORD("typeof", Token::TYPEOF) \
+ KEYWORD_GROUP('v') \
+ KEYWORD("var", Token::VAR) \
+ KEYWORD("void", Token::VOID) \
+ KEYWORD_GROUP('w') \
+ KEYWORD("while", Token::WHILE) \
+ KEYWORD("with", Token::WITH) \
+ KEYWORD_GROUP('y') \
+ KEYWORD("yield", Token::FUTURE_STRICT_RESERVED_WORD)
static Token::Value KeywordOrIdentifierToken(const char* input,
- int input_length) {
+ int input_length,
+ bool harmony_block_scoping) {
+
ASSERT(input_length >= 1);
const int kMinLength = 2;
const int kMaxLength = 10;
(keyword_length <= 7 || input[7] == keyword[7]) && \
(keyword_length <= 8 || input[8] == keyword[8]) && \
(keyword_length <= 9 || input[9] == keyword[9])) { \
- return Token::token; \
+ return token; \
} \
}
KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD)
if (next_.literal_chars->is_ascii()) {
Vector<const char> chars = next_.literal_chars->ascii_literal();
- return KeywordOrIdentifierToken(chars.start(), chars.length());
+ return KeywordOrIdentifierToken(chars.start(),
+ chars.length(),
+ harmony_block_scoping_);
}
return Token::IDENTIFIER;
// 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();
// 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
// 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);
}
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) \
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";
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)
__ 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
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());
}
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;
}
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();
--- /dev/null
+// 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();
--- /dev/null
+// 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;");
}
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();
scope_size--;
}
- // TODO(keuchel): print('count' + count + ' scopesize' + scope_size);
if (count != scope_size) {
print('Names found in scope:');
var names = scope.scopeObject().propertyNames();
BeginTest("Local 3");
function local_3(a) {
- var x = 3;
+ let x = 3;
debugger;
}
BeginTest("Local 4");
function local_4(a, b) {
- var x = 3;
- var y = 4;
+ let x = 3;
+ let y = 4;
debugger;
}
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();
// (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.