ArrowFormalParametersProduction)
};
+ enum FunctionProperties { NonSimpleParameter = 1 << 0 };
+
ExpressionClassifier()
- : invalid_productions_(0), duplicate_finder_(nullptr) {}
+ : invalid_productions_(0),
+ function_properties_(0),
+ duplicate_finder_(nullptr) {}
explicit ExpressionClassifier(DuplicateFinder* duplicate_finder)
- : invalid_productions_(0), duplicate_finder_(duplicate_finder) {}
+ : invalid_productions_(0),
+ function_properties_(0),
+ duplicate_finder_(duplicate_finder) {}
bool is_valid(unsigned productions) const {
return (invalid_productions_ & productions) == 0;
return strong_mode_formal_parameter_error_;
}
+ bool is_simple_parameter_list() const {
+ return !(function_properties_ & NonSimpleParameter);
+ }
+
+ void RecordNonSimpleParameter() {
+ function_properties_ |= NonSimpleParameter;
+ }
+
void RecordExpressionError(const Scanner::Location& loc,
MessageTemplate::Template message,
const char* arg = nullptr) {
// As an exception to the above, the result continues to be a valid arrow
// formal parameters if the inner expression is a valid binding pattern.
if (productions & ArrowFormalParametersProduction &&
- is_valid_arrow_formal_parameters() &&
- !inner.is_valid_binding_pattern()) {
- invalid_productions_ |= ArrowFormalParametersProduction;
- arrow_formal_parameters_error_ = inner.binding_pattern_error_;
+ is_valid_arrow_formal_parameters()) {
+ // Also copy function properties if expecting an arrow function
+ // parameter.
+ function_properties_ |= inner.function_properties_;
+
+ if (!inner.is_valid_binding_pattern()) {
+ invalid_productions_ |= ArrowFormalParametersProduction;
+ arrow_formal_parameters_error_ = inner.binding_pattern_error_;
+ }
}
}
private:
unsigned invalid_productions_;
+ unsigned function_properties_;
Error expression_error_;
Error binding_pattern_error_;
Error assignment_pattern_error_;
T(IllegalAccess, "Illegal access") \
T(IllegalBreak, "Illegal break statement") \
T(IllegalContinue, "Illegal continue statement") \
+ T(IllegalLanguageModeDirective, \
+ "Illegal '%' directive in function with non-simple parameter list") \
T(IllegalReturn, "Illegal return statement") \
T(InvalidLhsInAssignment, "Invalid left-hand side in assignment") \
T(InvalidLhsInFor, "Invalid left-hand side in for-loop") \
// BindingIdentifier
ParseFormalParameter(&formals, &formals_classifier, &ok);
if (ok) {
- DeclareFormalParameter(
- formals.scope, formals.at(0), formals.is_simple,
- &formals_classifier);
+ DeclareFormalParameter(formals.scope, formals.at(0),
+ &formals_classifier);
}
}
}
token_loc.end_pos - token_loc.beg_pos ==
ast_value_factory()->use_strong_string()->length() + 2;
if (use_strict_found || use_strong_found) {
+ if (!scope_->HasSimpleParameters()) {
+ // TC39 deemed "use strict" directives to be an error when occurring
+ // in the body of a function with non-simple parameter list, on
+ // 29/7/2015. https://goo.gl/ueA7Ln
+ //
+ // In V8, this also applies to "use strong " directives.
+ const AstRawString* string = literal->raw_value()->AsString();
+ ParserTraits::ReportMessageAt(
+ token_loc, MessageTemplate::kIllegalLanguageModeDirective,
+ string);
+ *ok = false;
+ return nullptr;
+ }
+
// Strong mode implies strict mode. If there are several "use strict"
// / "use strong" directives, do the strict mode changes only once.
if (is_sloppy(scope_->language_mode())) {
void ParserTraits::ParseArrowFunctionFormalParameters(
ParserFormalParameters* parameters, Expression* expr,
- const Scanner::Location& params_loc,
- Scanner::Location* duplicate_loc, bool* ok) {
+ const Scanner::Location& params_loc, bool* ok) {
if (parameters->Arity() >= Code::kMaxArguments) {
ReportMessageAt(params_loc, MessageTemplate::kMalformedArrowFunParamList);
*ok = false;
DCHECK_EQ(binop->op(), Token::COMMA);
Expression* left = binop->left();
Expression* right = binop->right();
- ParseArrowFunctionFormalParameters(parameters, left, params_loc,
- duplicate_loc, ok);
+ ParseArrowFunctionFormalParameters(parameters, left, params_loc, ok);
if (!*ok) return;
// LHS of comma expression should be unparenthesized.
expr = right;
Scanner::Location* duplicate_loc, bool* ok) {
if (expr->IsEmptyParentheses()) return;
- ParseArrowFunctionFormalParameters(parameters, expr, params_loc,
- duplicate_loc, ok);
+ ParseArrowFunctionFormalParameters(parameters, expr, params_loc, ok);
if (!*ok) return;
+ ExpressionClassifier classifier;
+ if (!parameters->is_simple) {
+ classifier.RecordNonSimpleParameter();
+ }
for (int i = 0; i < parameters->Arity(); ++i) {
auto parameter = parameters->at(i);
- ExpressionClassifier classifier;
- DeclareFormalParameter(
- parameters->scope, parameter, parameters->is_simple, &classifier);
+ DeclareFormalParameter(parameters->scope, parameter, &classifier);
if (!duplicate_loc->IsValid()) {
*duplicate_loc = classifier.duplicate_formal_parameter_error().location;
}
#undef SET_ALLOW
}
PreParser::PreParseResult result = reusable_preparser_->PreParseLazyFunction(
- language_mode(), function_state_->kind(), logger, bookmark);
+ language_mode(), function_state_->kind(), scope_->has_simple_parameters(),
+ logger, bookmark);
if (pre_parse_timer_ != NULL) {
pre_parse_timer_->Stop();
}
Expression* initializer, bool is_rest);
V8_INLINE void DeclareFormalParameter(
Scope* scope, const ParserFormalParameters::Parameter& parameter,
- bool is_simple, ExpressionClassifier* classifier);
- void ParseArrowFunctionFormalParameters(
- ParserFormalParameters* parameters, Expression* params,
- const Scanner::Location& params_loc,
- Scanner::Location* duplicate_loc, bool* ok);
+ ExpressionClassifier* classifier);
+ void ParseArrowFunctionFormalParameters(ParserFormalParameters* parameters,
+ Expression* params,
+ const Scanner::Location& params_loc,
+ bool* ok);
void ParseArrowFunctionFormalParameterList(
ParserFormalParameters* parameters, Expression* params,
const Scanner::Location& params_loc,
void ParserTraits::DeclareFormalParameter(
Scope* scope, const ParserFormalParameters::Parameter& parameter,
- bool is_simple, ExpressionClassifier* classifier) {
+ ExpressionClassifier* classifier) {
bool is_duplicate = false;
+ bool is_simple = classifier->is_simple_parameter_list();
// TODO(caitp): Remove special handling for rest once desugaring is in.
auto name = is_simple || parameter.is_rest
? parameter.name : parser_->ast_value_factory()->empty_string();
auto mode = is_simple || parameter.is_rest ? VAR : TEMPORARY;
+ if (!is_simple) scope->SetHasNonSimpleParameters();
bool is_optional = parameter.initializer != nullptr;
Variable* var = scope->DeclareParameter(
name, mode, is_optional, parameter.is_rest, &is_duplicate);
PreParser::PreParseResult PreParser::PreParseLazyFunction(
- LanguageMode language_mode, FunctionKind kind, ParserRecorder* log,
- Scanner::BookmarkScope* bookmark) {
+ LanguageMode language_mode, FunctionKind kind, bool has_simple_parameters,
+ ParserRecorder* log, Scanner::BookmarkScope* bookmark) {
log_ = log;
// Lazy functions always have trivial outer scopes (no with/catch scopes).
Scope* top_scope = NewScope(scope_, SCRIPT_SCOPE);
scope_->SetLanguageMode(language_mode);
Scope* function_scope = NewScope(
scope_, IsArrowFunction(kind) ? ARROW_SCOPE : FUNCTION_SCOPE, kind);
+ if (!has_simple_parameters) function_scope->SetHasNonSimpleParameters();
PreParserFactory function_factory(NULL);
FunctionState function_state(&function_state_, &scope_, function_scope, kind,
&function_factory);
}
if (directive_prologue) {
- if (statement.IsUseStrictLiteral()) {
+ bool use_strict_found = statement.IsUseStrictLiteral();
+ bool use_strong_found =
+ statement.IsUseStrongLiteral() && allow_strong_mode();
+
+ if (use_strict_found) {
scope_->SetLanguageMode(
static_cast<LanguageMode>(scope_->language_mode() | STRICT));
- } else if (statement.IsUseStrongLiteral() && allow_strong_mode()) {
+ } else if (use_strong_found) {
scope_->SetLanguageMode(static_cast<LanguageMode>(
scope_->language_mode() | STRONG));
} else if (!statement.IsStringLiteral()) {
directive_prologue = false;
}
+
+ if ((use_strict_found || use_strong_found) &&
+ !scope_->HasSimpleParameters()) {
+ // TC39 deemed "use strict" directives to be an error when occurring
+ // in the body of a function with non-simple parameter list, on
+ // 29/7/2015. https://goo.gl/ueA7Ln
+ //
+ // In V8, this also applies to "use strong " directives.
+ PreParserTraits::ReportMessageAt(
+ token_loc, MessageTemplate::kIllegalLanguageModeDirective,
+ use_strict_found ? "use strict" : "use strong");
+ *ok = false;
+ return;
+ }
}
// If we're allowed to reset to a bookmark, we will do so when we see a long
Expect(Token::LPAREN, CHECK_OK);
int start_position = scanner()->location().beg_pos;
function_scope->set_start_position(start_position);
- PreParserFormalParameters formals(nullptr);
+ PreParserFormalParameters formals(function_scope);
ParseFormalParameterList(&formals, &formals_classifier, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
int formals_end_position = scanner()->location().end_pos;
++parameters->arity;
}
void DeclareFormalParameter(Scope* scope, PreParserIdentifier parameter,
- bool is_simple,
- ExpressionClassifier* classifier) {}
+ ExpressionClassifier* classifier) {
+ if (!classifier->is_simple_parameter_list()) {
+ scope->SetHasNonSimpleParameters();
+ }
+ }
void CheckConflictingVarDeclarations(Scope* scope, bool* ok) {}
// At return, unless an error occurred, the scanner is positioned before the
// the final '}'.
PreParseResult PreParseLazyFunction(
- LanguageMode language_mode, FunctionKind kind, ParserRecorder* log,
- Scanner::BookmarkScope* bookmark = nullptr);
+ LanguageMode language_mode, FunctionKind kind, bool has_simple_parameters,
+ ParserRecorder* log, Scanner::BookmarkScope* bookmark = nullptr);
private:
friend class PreParserTraits;
classifier->RecordExpressionError(scanner()->location(),
MessageTemplate::kUnexpectedToken,
Token::String(Token::ELLIPSIS));
+ classifier->RecordNonSimpleParameter();
Scanner::Location expr_loc = scanner()->peek_location();
Token::Value tok = peek();
result = this->ParseAssignmentExpression(true, classifier, CHECK_OK);
return this->EmptyExpression();
}
result = factory()->NewSpread(result, ellipsis_pos);
+
if (peek() == Token::COMMA) {
ReportMessageAt(scanner()->peek_location(),
MessageTemplate::kParamAfterRest);
this->ParseAssignmentExpression(accept_IN, &binding_classifier, CHECK_OK);
classifier->Accumulate(binding_classifier,
ExpressionClassifier::AllProductions);
+ bool is_simple_parameter_list = this->IsIdentifier(result);
bool seen_rest = false;
while (peek() == Token::COMMA) {
if (seen_rest) {
ExpressionT right = this->ParseAssignmentExpression(
accept_IN, &binding_classifier, CHECK_OK);
if (is_rest) right = factory()->NewSpread(right, pos);
+ is_simple_parameter_list =
+ is_simple_parameter_list && this->IsIdentifier(right);
classifier->Accumulate(binding_classifier,
ExpressionClassifier::AllProductions);
result = factory()->NewBinaryOperation(Token::COMMA, result, right, pos);
}
+ if (!is_simple_parameter_list || seen_rest) {
+ classifier->RecordNonSimpleParameter();
+ }
return result;
}
}
ExpressionT expression = this->ParseConditionalExpression(
accept_IN, &arrow_formals_classifier, CHECK_OK);
-
if (allow_harmony_arrow_functions() && peek() == Token::ARROW) {
BindingPatternUnexpectedToken(classifier);
ValidateArrowFormalParameters(&arrow_formals_classifier, expression,
Scope* scope =
this->NewScope(scope_, ARROW_SCOPE, FunctionKind::kArrowFunction);
FormalParametersT parameters(scope);
+ if (!arrow_formals_classifier.is_simple_parameter_list()) {
+ scope->SetHasNonSimpleParameters();
+ parameters.is_simple = false;
+ }
checkpoint.Restore(¶meters.materialized_literals_count);
scope->set_start_position(lhs_beg_pos);
return;
}
parameters->is_simple = false;
+ classifier->RecordNonSimpleParameter();
}
ExpressionT initializer = Traits::EmptyExpression();
ValidateExpression(&init_classifier, ok);
if (!*ok) return;
parameters->is_simple = false;
+ classifier->RecordNonSimpleParameter();
}
Traits::AddFormalParameter(parameters, pattern, initializer, is_rest);
if (parameters->has_rest) {
parameters->is_simple = false;
+ classifier->RecordNonSimpleParameter();
if (peek() == Token::COMMA) {
ReportMessageAt(scanner()->peek_location(),
MessageTemplate::kParamAfterRest);
for (int i = 0; i < parameters->Arity(); ++i) {
auto parameter = parameters->at(i);
- Traits::DeclareFormalParameter(
- parameters->scope, parameter, parameters->is_simple, classifier);
+ Traits::DeclareFormalParameter(parameters->scope, parameter, classifier);
}
}
Variable* var;
if (mode == TEMPORARY) {
var = NewTemporary(name);
- has_simple_parameters_ = false;
} else {
var = variables_.Declare(this, name, mode, Variable::NORMAL,
kCreatedInitialized);
DCHECK_NULL(rest_parameter_);
rest_parameter_ = var;
rest_index_ = num_parameters();
- has_simple_parameters_ = false;
}
params_.Add(var, zone());
return var;
}
bool has_simple_parameters() const {
- DCHECK(is_function_scope());
return has_simple_parameters_;
}
+ // TODO(caitp): manage this state in a better way. PreParser must be able to
+ // communicate that the scope is non-simple, without allocating any parameters
+ // as the Parser does. This is necessary to ensure that TC39's proposed early
+ // error can be reported consistently regardless of whether lazily parsed or
+ // not.
+ void SetHasNonSimpleParameters() {
+ DCHECK(is_function_scope());
+ has_simple_parameters_ = false;
+ }
+
+ // Retrieve `IsSimpleParameterList` of current or outer function.
+ bool HasSimpleParameters() {
+ Scope* scope = ClosureScope();
+ return !scope->is_function_scope() || scope->has_simple_parameters();
+ }
+
// The local variable 'arguments' if we need to allocate it; NULL otherwise.
Variable* arguments() const {
DCHECK(!is_arrow_scope() || arguments_ == nullptr);
RunParserSyncTest(context_data, data, kSuccess, NULL, 0, always_flags,
arraysize(always_flags));
}
+
+
+TEST(LanguageModeDirectivesNonSimpleParameterListErrors) {
+ // TC39 deemed "use strict" directives to be an error when occurring in the
+ // body of a function with non-simple parameter list, on 29/7/2015.
+ // https://goo.gl/ueA7Ln
+ //
+ // In V8, this also applies to "use strong " directives.
+ const char* context_data[][2] = {
+ {"function f(", ") { 'use strict'; }"},
+ {"function f(", ") { 'use strong'; }"},
+ {"function* g(", ") { 'use strict'; }"},
+ {"function* g(", ") { 'use strong'; }"},
+ {"class c { foo(", ") { 'use strict' }"},
+ {"class c { foo(", ") { 'use strong' }"},
+ {"var a = (", ") => { 'use strict'; }"},
+ {"var a = (", ") => { 'use strong'; }"},
+ {"var o = { m(", ") { 'use strict'; }"},
+ {"var o = { m(", ") { 'use strong'; }"},
+ {"var o = { *gm(", ") { 'use strict'; }"},
+ {"var o = { *gm(", ") { 'use strong'; }"},
+ {"var c = { m(", ") { 'use strict'; }"},
+ {"var c = { m(", ") { 'use strong'; }"},
+ {"var c = { *gm(", ") { 'use strict'; }"},
+ {"var c = { *gm(", ") { 'use strong'; }"},
+
+ {"'use strict'; function f(", ") { 'use strict'; }"},
+ {"'use strict'; function f(", ") { 'use strong'; }"},
+ {"'use strict'; function* g(", ") { 'use strict'; }"},
+ {"'use strict'; function* g(", ") { 'use strong'; }"},
+ {"'use strict'; class c { foo(", ") { 'use strict' }"},
+ {"'use strict'; class c { foo(", ") { 'use strong' }"},
+ {"'use strict'; var a = (", ") => { 'use strict'; }"},
+ {"'use strict'; var a = (", ") => { 'use strong'; }"},
+ {"'use strict'; var o = { m(", ") { 'use strict'; }"},
+ {"'use strict'; var o = { m(", ") { 'use strong'; }"},
+ {"'use strict'; var o = { *gm(", ") { 'use strict'; }"},
+ {"'use strict'; var o = { *gm(", ") { 'use strong'; }"},
+ {"'use strict'; var c = { m(", ") { 'use strict'; }"},
+ {"'use strict'; var c = { m(", ") { 'use strong'; }"},
+ {"'use strict'; var c = { *gm(", ") { 'use strict'; }"},
+ {"'use strict'; var c = { *gm(", ") { 'use strong'; }"},
+
+ {"'use strong'; function f(", ") { 'use strict'; }"},
+ {"'use strong'; function f(", ") { 'use strong'; }"},
+ {"'use strong'; function* g(", ") { 'use strict'; }"},
+ {"'use strong'; function* g(", ") { 'use strong'; }"},
+ {"'use strong'; class c { foo(", ") { 'use strict' }"},
+ {"'use strong'; class c { foo(", ") { 'use strong' }"},
+ {"'use strong'; var a = (", ") => { 'use strict'; }"},
+ {"'use strong'; var a = (", ") => { 'use strong'; }"},
+ {"'use strong'; var o = { m(", ") { 'use strict'; }"},
+ {"'use strong'; var o = { m(", ") { 'use strong'; }"},
+ {"'use strong'; var o = { *gm(", ") { 'use strict'; }"},
+ {"'use strong'; var o = { *gm(", ") { 'use strong'; }"},
+ {"'use strong'; var c = { m(", ") { 'use strict'; }"},
+ {"'use strong'; var c = { m(", ") { 'use strong'; }"},
+ {"'use strong'; var c = { *gm(", ") { 'use strict'; }"},
+ {"'use strong'; var c = { *gm(", ") { 'use strong'; }"},
+
+ {NULL, NULL}};
+
+ const char* data[] = {
+ // TODO(@caitp): support formal parameter initializers
+ "{}",
+ "[]",
+ "[{}]",
+ "{a}",
+ "a, {b}",
+ "a, b, {c, d, e}",
+ "initializer = true",
+ "a, b, c = 1",
+ "...args",
+ "a, b, ...rest",
+ "[a, b, ...rest]",
+ "{ bindingPattern = {} }",
+ "{ initializedBindingPattern } = { initializedBindingPattern: true }",
+ NULL};
+
+ static const ParserFlag always_flags[] = {
+ kAllowHarmonyArrowFunctions, kAllowHarmonyDefaultParameters,
+ kAllowHarmonyDestructuring, kAllowHarmonyRestParameters,
+ kAllowHarmonySloppy, kAllowStrongMode
+ };
+ RunParserSyncTest(context_data, data, kError, NULL, 0, always_flags,
+ arraysize(always_flags));
+}
//
// strictTest(6,5,4,3,2,1)
//
-var strictTest = (a, b, ...c) => {
+var strictTest = (() => {
"use strict";
- assertEquals(Array, c.constructor);
- assertTrue(Array.isArray(c));
+ return (a, b, ...c) => {
+ assertEquals(Array, c.constructor);
+ assertTrue(Array.isArray(c));
- var expectedLength = (a === undefined) ? 0 : a - 2;
- assertEquals(expectedLength, c.length);
+ var expectedLength = (a === undefined) ? 0 : a - 2;
+ assertEquals(expectedLength, c.length);
- for (var i = 2; i < a; ++i) {
- assertEquals(c[i - 2], a - i);
- }
-}
+ for (var i = 2; i < a; ++i) {
+ assertEquals(c[i - 2], a - i);
+ }
+ };
+})();
var sloppyTest = (a, b, ...c) => {
assertEquals(Array, c.constructor);
})();
-(function TestParameterScoping() {
+(function TestParameterScopingSloppy() {
var x = 1;
function f1(a = x) { var x = 2; return a; }
assertEquals(1, f1());
function f2(a = x) { function x() {}; return a; }
assertEquals(1, f2());
- function f3(a = x) { 'use strict'; let x = 2; return a; }
+ function f3(a = eval("x")) { var x; return a; }
assertEquals(1, f3());
- function f4(a = x) { 'use strict'; const x = 2; return a; }
+ function f31(a = eval("'use strict'; x")) { var x; return a; }
+ assertEquals(1, f31());
+ function f4(a = function() { return x }) { var x; return a(); }
assertEquals(1, f4());
- function f5(a = x) { 'use strict'; function x() {}; return a; }
+ function f5(a = () => x) { var x; return a(); }
assertEquals(1, f5());
- function f6(a = eval("x")) { var x; return a; }
+ function f6(a = () => eval("x")) { var x; return a(); }
assertEquals(1, f6());
- function f61(a = eval("x")) { 'use strict'; var x; return a; }
+ function f61(a = () => { 'use strict'; return eval("x") }) { var x; return a(); }
assertEquals(1, f61());
- function f62(a = eval("'use strict'; x")) { var x; return a; }
+ function f62(a = () => eval("'use strict'; x")) { var x; return a(); }
assertEquals(1, f62());
- function f7(a = function() { return x }) { var x; return a(); }
- assertEquals(1, f7());
- function f8(a = () => x) { var x; return a(); }
- assertEquals(1, f8());
- function f9(a = () => eval("x")) { var x; return a(); }
- assertEquals(1, f9());
- function f91(a = () => eval("x")) { 'use strict'; var x; return a(); }
- assertEquals(1, f91());
- function f92(a = () => { 'use strict'; return eval("x") }) { var x; return a(); }
- assertEquals(1, f92());
- function f93(a = () => eval("'use strict'; x")) { var x; return a(); }
- assertEquals(1, f93());
var g1 = (a = x) => { var x = 2; return a; };
assertEquals(1, g1());
var g2 = (a = x) => { function x() {}; return a; };
assertEquals(1, g2());
- var g3 = (a = x) => { 'use strict'; let x = 2; return a; };
+ var g3 = (a = eval("x")) => { var x; return a; };
assertEquals(1, g3());
- var g4 = (a = x) => { 'use strict'; const x = 2; return a; };
+ var g31 = (a = eval("'use strict'; x")) => { var x; return a; };
+ assertEquals(1, g31());
+ var g4 = (a = function() { return x }) => { var x; return a(); };
assertEquals(1, g4());
- var g5 = (a = x) => { 'use strict'; function x() {}; return a; };
+ var g5 = (a = () => x) => { var x; return a(); };
assertEquals(1, g5());
- var g6 = (a = eval("x")) => { var x; return a; };
+ var g6 = (a = () => eval("x")) => { var x; return a(); };
assertEquals(1, g6());
- var g61 = (a = eval("x")) => { 'use strict'; var x; return a; };
+ var g61 = (a = () => { 'use strict'; return eval("x") }) => { var x; return a(); };
assertEquals(1, g61());
- var g62 = (a = eval("'use strict'; x")) => { var x; return a; };
+ var g62 = (a = () => eval("'use strict'; x")) => { var x; return a(); };
assertEquals(1, g62());
- var g7 = (a = function() { return x }) => { var x; return a(); };
- assertEquals(1, g7());
- var g8 = (a = () => x) => { var x; return a(); };
- assertEquals(1, g8());
- var g9 = (a = () => eval("x")) => { var x; return a(); };
- assertEquals(1, g9());
- var g91 = (a = () => eval("x")) => { 'use strict'; var x; return a(); };
- assertEquals(1, g91());
- var g92 = (a = () => { 'use strict'; return eval("x") }) => { var x; return a(); };
- assertEquals(1, g92());
- var g93 = (a = () => eval("'use strict'; x")) => { var x; return a(); };
- assertEquals(1, g93());
var f11 = function f(x = f) { var f; return x; }
assertSame(f11, f11());
var f12 = function f(x = f) { function f() {}; return x; }
assertSame(f12, f12());
- var f13 = function f(x = f) { 'use strict'; let f; return x; }
- assertSame(f13, f13());
- var f14 = function f(x = f) { 'use strict'; const f = 0; return x; }
- assertSame(f14, f14());
- var f15 = function f(x = f) { 'use strict'; function f() {}; return x; }
- assertSame(f15, f15());
- var f16 = function f(f = 7, x = f) { return x; }
- assertSame(7, f16());
+ var f13 = function f(f = 7, x = f) { return x; }
+ assertSame(7, f13());
var o1 = {f: function(x = this) { return x; }};
assertSame(o1, o1.f());
assertSame(1, o1.f(1));
})();
+(function TestParameterScopingStrict() {
+ "use strict";
+ var x = 1;
+
+ function f1(a = x) { let x = 2; return a; }
+ assertEquals(1, f1());
+ function f2(a = x) { const x = 2; return a; }
+ assertEquals(1, f2());
+ function f3(a = x) { function x() {}; return a; }
+ assertEquals(1, f3());
+ function f4(a = eval("x")) { var x; return a; }
+ assertEquals(1, f4());
+ function f5(a = () => eval("x")) { var x; return a(); }
+ assertEquals(1, f5());
+
+ var g1 = (a = x) => { let x = 2; return a; };
+ assertEquals(1, g1());
+ var g2 = (a = x) => { const x = 2; return a; };
+ assertEquals(1, g2());
+ var g3 = (a = x) => { function x() {}; return a; };
+ assertEquals(1, g3());
+ var g4 = (a = eval("x")) => { var x; return a; };
+ assertEquals(1, g4());
+ var g5 = (a = () => eval("x")) => { var x; return a(); };
+ assertEquals(1, g5());
+
+ var f11 = function f(x = f) { let f; return x; }
+ assertSame(f11, f11());
+ var f12 = function f(x = f) { const f = 0; return x; }
+ assertSame(f12, f12());
+ var f13 = function f(x = f) { function f() {}; return x; }
+ assertSame(f13, f13());
+})();
(function TestSloppyEvalScoping() {
var x = 1;
assertEquals(3, f22(() => 3));
})();
-
-(function TestParameterTDZ() {
+(function TestParameterTDZSloppy() {
function f1(a = x, x) { return a }
assertThrows(() => f1(undefined, 4), ReferenceError);
assertEquals(4, f1(4, 5));
function f2(a = eval("x"), x) { return a }
assertThrows(() => f2(undefined, 4), ReferenceError);
assertEquals(4, f2(4, 5));
- function f3(a = eval("x"), x) { 'use strict'; return a }
+ function f3(a = eval("'use strict'; x"), x) { return a }
assertThrows(() => f3(undefined, 4), ReferenceError);
assertEquals(4, f3(4, 5));
- function f4(a = eval("'use strict'; x"), x) { return a }
- assertThrows(() => f4(undefined, 4), ReferenceError);
- assertEquals(4, f4(4, 5));
-
- function f5(a = () => x, x) { return a() }
+ function f4(a = () => x, x) { return a() }
+ assertEquals(4, f4(() => 4, 5));
+ function f5(a = () => eval("x"), x) { return a() }
assertEquals(4, f5(() => 4, 5));
- function f6(a = () => eval("x"), x) { return a() }
+ function f6(a = () => eval("'use strict'; x"), x) { return a() }
assertEquals(4, f6(() => 4, 5));
- function f7(a = () => eval("x"), x) { 'use strict'; return a() }
- assertEquals(4, f7(() => 4, 5));
- function f8(a = () => eval("'use strict'; x"), x) { return a() }
- assertEquals(4, f8(() => 4, 5));
function f11(a = x, x = 2) { return a }
assertThrows(() => f11(), ReferenceError);
assertThrows(() => f12(undefined), ReferenceError);
assertThrows(() => f12(undefined, 4), ReferenceError);
assertEquals(4, f12(4, 5));
- function f13(a = eval("x"), x = 2) { 'use strict'; return a }
+ function f13(a = eval("'use strict'; x"), x = 2) { return a }
assertThrows(() => f13(), ReferenceError);
assertThrows(() => f13(undefined), ReferenceError);
assertThrows(() => f13(undefined, 4), ReferenceError);
assertEquals(4, f13(4, 5));
- function f14(a = eval("'use strict'; x"), x = 2) { return a }
- assertThrows(() => f14(), ReferenceError);
- assertThrows(() => f14(undefined), ReferenceError);
- assertThrows(() => f14(undefined, 4), ReferenceError);
- assertEquals(4, f14(4, 5));
-
- function f34(x = function() { return a }, ...a) { return x()[0] }
- assertEquals(4, f34(undefined, 4));
- function f35(x = () => a, ...a) { return x()[0] }
- assertEquals(4, f35(undefined, 4));
- function f36(x = () => eval("a"), ...a) { return x()[0] }
- assertEquals(4, f36(undefined, 4));
- function f37(x = () => eval("a"), ...a) { 'use strict'; return x()[0] }
- assertEquals(4, f37(undefined, 4));
- function f38(x = () => { 'use strict'; return eval("a") }, ...a) { return x()[0] }
- assertEquals(4, f38(undefined, 4));
- function f39(x = () => eval("'use strict'; a"), ...a) { return x()[0] }
- assertEquals(4, f39(undefined, 4));
-
- var g34 = (x = function() { return a }, ...a) => { return x()[0] };
- assertEquals(4, g34(undefined, 4));
- var g35 = (x = () => a, ...a) => { return x()[0] };
- assertEquals(4, g35(undefined, 4));
+
+ function f21(x = function() { return a }, ...a) { return x()[0] }
+ assertEquals(4, f21(undefined, 4));
+ function f22(x = () => a, ...a) { return x()[0] }
+ assertEquals(4, f22(undefined, 4));
+ function f23(x = () => eval("a"), ...a) { return x()[0] }
+ assertEquals(4, f23(undefined, 4));
+ function f24(x = () => {'use strict'; return eval("a") }, ...a) {
+ return x()[0]
+ }
+ assertEquals(4, f24(undefined, 4));
+ function f25(x = () => eval("'use strict'; a"), ...a) { return x()[0] }
+ assertEquals(4, f25(undefined, 4));
+
+ var g1 = (x = function() { return a }, ...a) => { return x()[0] };
+ assertEquals(4, g1(undefined, 4));
+ var g2 = (x = () => a, ...a) => { return x()[0] };
+ assertEquals(4, g2(undefined, 4));
})();
+(function TestParameterTDZStrict() {
+ "use strict";
+
+ function f1(a = eval("x"), x) { return a }
+ assertThrows(() => f1(undefined, 4), ReferenceError);
+ assertEquals(4, f1(4, 5));
+ function f2(a = () => eval("x"), x) { return a() }
+ assertEquals(4, f2(() => 4, 5));
+
+ function f11(a = eval("x"), x = 2) { return a }
+ assertThrows(() => f11(), ReferenceError);
+ assertThrows(() => f11(undefined), ReferenceError);
+ assertThrows(() => f11(undefined, 4), ReferenceError);
+ assertEquals(4, f11(4, 5));
+
+ function f21(x = () => eval("a"), ...a) { return x()[0] }
+ assertEquals(4, f21(undefined, 4));
+})();
(function TestArgumentsForNonSimpleParameters() {
function f1(x = 900) { arguments[0] = 1; return x }
assertEquals(1, (function(x, y = 1, z, v = 2) {}).length);
assertEquals(1, (function(x, y = 1, z, v = 2, ...a) {}).length);
})();
+
+(function TestDirectiveThrows() {
+ "use strict";
+
+ assertThrows(function(){ eval("function(x=1){'use strict';}") }, SyntaxError);
+ assertThrows(function(){ eval("(x=1) => {'use strict';}") }, SyntaxError);
+ assertThrows(
+ function(){ eval("(class{foo(x=1) {'use strict';}});") }, SyntaxError);
+
+ assertThrows(
+ function(){ eval("function(a, x=1){'use strict';}") }, SyntaxError);
+ assertThrows(function(){ eval("(a, x=1) => {'use strict';}") }, SyntaxError);
+ assertThrows(
+ function(){ eval("(class{foo(a, x=1) {'use strict';}});") }, SyntaxError);
+})();
assertEquals(1, f1({}));
function f2({a = x}) { function x() {}; return a; }
assertEquals(1, f2({}));
- function f3({a = x}) { 'use strict'; let x = 2; return a; }
- assertEquals(1, f3({}));
- function f4({a = x}) { 'use strict'; const x = 2; return a; }
- assertEquals(1, f4({}));
- function f5({a = x}) { 'use strict'; function x() {}; return a; }
- assertEquals(1, f5({}));
+ (function() {
+ 'use strict';
+ function f3({a = x}) { let x = 2; return a; }
+ assertEquals(1, f3({}));
+ function f4({a = x}) { const x = 2; return a; }
+ assertEquals(1, f4({}));
+ function f5({a = x}) { function x() {}; return a; }
+ assertEquals(1, f5({}));
+ })();
function f6({a = eval("x")}) { var x; return a; }
assertEquals(1, f6({}));
- function f61({a = eval("x")}) { 'use strict'; var x; return a; }
- assertEquals(1, f61({}));
+ (function() {
+ 'use strict';
+ function f61({a = eval("x")}) { var x; return a; }
+ assertEquals(1, f61({}));
+ })();
function f62({a = eval("'use strict'; x")}) { var x; return a; }
assertEquals(1, f62({}));
function f7({a = function() { return x }}) { var x; return a(); }
assertEquals(1, f8({}));
function f9({a = () => eval("x")}) { var x; return a(); }
assertEquals(1, f9({}));
- function f91({a = () => eval("x")}) { 'use strict'; var x; return a(); }
- assertEquals(1, f91({}));
+ (function TestInitializedWithEvalArrowStrict() {
+ 'use strict';
+ function f91({a = () => eval("x")}) { var x; return a(); }
+ assertEquals(1, f91({}));
+ })();
function f92({a = () => { 'use strict'; return eval("x") }}) { var x; return a(); }
assertEquals(1, f92({}));
function f93({a = () => eval("'use strict'; x")}) { var x; return a(); }
assertEquals(1, g1({}));
var g2 = ({a = x}) => { function x() {}; return a; };
assertEquals(1, g2({}));
- var g3 = ({a = x}) => { 'use strict'; let x = 2; return a; };
- assertEquals(1, g3({}));
- var g4 = ({a = x}) => { 'use strict'; const x = 2; return a; };
- assertEquals(1, g4({}));
- var g5 = ({a = x}) => { 'use strict'; function x() {}; return a; };
- assertEquals(1, g5({}));
+ (function() {
+ 'use strict';
+ var g3 = ({a = x}) => { let x = 2; return a; };
+ assertEquals(1, g3({}));
+ var g4 = ({a = x}) => { const x = 2; return a; };
+ assertEquals(1, g4({}));
+ var g5 = ({a = x}) => { function x() {}; return a; };
+ assertEquals(1, g5({}));
+ })();
var g6 = ({a = eval("x")}) => { var x; return a; };
assertEquals(1, g6({}));
- var g61 = ({a = eval("x")}) => { 'use strict'; var x; return a; };
- assertEquals(1, g61({}));
+ (function() {
+ 'use strict';
+ var g61 = ({a = eval("x")}) => { var x; return a; };
+ assertEquals(1, g61({}));
+ })();
var g62 = ({a = eval("'use strict'; x")}) => { var x; return a; };
assertEquals(1, g62({}));
var g7 = ({a = function() { return x }}) => { var x; return a(); };
assertEquals(1, g8({}));
var g9 = ({a = () => eval("x")}) => { var x; return a(); };
assertEquals(1, g9({}));
- var g91 = ({a = () => eval("x")}) => { 'use strict'; var x; return a(); };
- assertEquals(1, g91({}));
- var g92 = ({a = () => { 'use strict'; return eval("x") }}) => { var x; return a(); };
- assertEquals(1, g92({}));
+ (function() {
+ 'use strict';
+ var g91 = ({a = () => eval("x")}) => { var x; return a(); };
+ assertEquals(1, g91({}));
+ var g92 = ({a = () => { return eval("x") }}) => { var x; return a(); };
+ assertEquals(1, g92({}));
+ })();
var g93 = ({a = () => eval("'use strict'; x")}) => { var x; return a(); };
assertEquals(1, g93({}));
assertSame(f11, f11({}));
var f12 = function f({x = f}) { function f() {}; return x; }
assertSame(f12, f12({}));
- var f13 = function f({x = f}) { 'use strict'; let f; return x; }
- assertSame(f13, f13({}));
- var f14 = function f({x = f}) { 'use strict'; const f = 0; return x; }
- assertSame(f14, f14({}));
- var f15 = function f({x = f}) { 'use strict'; function f() {}; return x; }
- assertSame(f15, f15({}));
+ (function() {
+ 'use strict';
+ var f13 = function f({x = f}) { let f; return x; }
+ assertSame(f13, f13({}));
+ var f14 = function f({x = f}) { const f = 0; return x; }
+ assertSame(f14, f14({}));
+ var f15 = function f({x = f}) { function f() {}; return x; }
+ assertSame(f15, f15({}));
+ })();
var f16 = function f({f = 7, x = f}) { return x; }
assertSame(7, f16({}));
function f2({a = eval("x")}, x) { return a }
assertThrows(() => f2({}, 4), ReferenceError);
assertEquals(4, f2({a: 4}, 5));
- function f3({a = eval("x")}, x) { 'use strict'; return a }
- assertThrows(() => f3({}, 4), ReferenceError);
- assertEquals(4, f3({a: 4}, 5));
+ (function() {
+ 'use strict';
+ function f3({a = eval("x")}, x) { return a }
+ assertThrows(() => f3({}, 4), ReferenceError);
+ assertEquals(4, f3({a: 4}, 5));
+ })();
function f4({a = eval("'use strict'; x")}, x) { return a }
assertThrows(() => f4({}, 4), ReferenceError);
assertEquals(4, f4({a: 4}, 5));
assertEquals(4, f5({a: () => 4}, 5));
function f6({a = () => eval("x")}, x) { return a() }
assertEquals(4, f6({a: () => 4}, 5));
- function f7({a = () => eval("x")}, x) { 'use strict'; return a() }
- assertEquals(4, f7({a: () => 4}, 5));
+ (function() {
+ 'use strict';
+ function f7({a = () => eval("x")}, x) { return a() }
+ assertEquals(4, f7({a: () => 4}, 5));
+ })();
function f8({a = () => eval("'use strict'; x")}, x) { return a() }
assertEquals(4, f8({a: () => 4}, 5));
function f12({a = eval("b")}, {b}) { return a }
assertThrows(() => f12({}, {b: 4}), ReferenceError);
assertEquals(4, f12({a: 4}, {b: 5}));
- function f13({a = eval("b")}, {b}) { 'use strict'; return a }
- assertThrows(() => f13({}, {b: 4}), ReferenceError);
- assertEquals(4, f13({a: 4}, {b: 5}));
+ (function() {
+ 'use strict';
+ function f13({a = eval("b")}, {b}) { return a }
+ assertThrows(() => f13({}, {b: 4}), ReferenceError);
+ assertEquals(4, f13({a: 4}, {b: 5}));
+ })();
function f14({a = eval("'use strict'; b")}, {b}) { return a }
assertThrows(() => f14({}, {b: 4}), ReferenceError);
assertEquals(4, f14({a: 4}, {b: 5}));
assertEquals(4, f15({a: () => 4}, {b: 5}));
function f16({a = () => eval("b")}, {b}) { return a() }
assertEquals(4, f16({a: () => 4}, {b: 5}));
- function f17({a = () => eval("b")}, {b}) { 'use strict'; return a() }
- assertEquals(4, f17({a: () => 4}, {b: 5}));
+ (function() {
+ 'use strict';
+ function f17({a = () => eval("b")}, {b}) { return a() }
+ assertEquals(4, f17({a: () => 4}, {b: 5}));
+ })();
function f18({a = () => eval("'use strict'; b")}, {b}) { return a() }
assertEquals(4, f18({a: () => 4}, {b: 5}));
assertEquals(4, f35({}, 4));
function f36({x = () => eval("a")}, ...a) { return x()[0] }
assertEquals(4, f36({}, 4));
- function f37({x = () => eval("a")}, ...a) { 'use strict'; return x()[0] }
- assertEquals(4, f37({}, 4));
+ (function() {
+ 'use strict';
+ function f37({x = () => eval("a")}, ...a) { return x()[0] }
+ assertEquals(4, f37({}, 4));
+ })();
function f38({x = () => { 'use strict'; return eval("a") }}, ...a) { return x()[0] }
assertEquals(4, f38({}, 4));
function f39({x = () => eval("'use strict'; a")}, ...a) { return x()[0] }
assertEquals(1, (function(x, {y} = {}, {z}, {v} = {}) {}).length);
assertEquals(1, (function({x}, {y} = {}, {z}, {v} = {}, ...a) {}).length);
})();
+
+
+(function TestDirectiveThrows() {
+ "use strict";
+
+ assertThrows(function(){ eval("function({}){'use strict';}") }, SyntaxError);
+ assertThrows(function(){ eval("({}) => {'use strict';}") }, SyntaxError);
+ assertThrows(
+ function(){ eval("(class{foo({}) {'use strict';}});") }, SyntaxError);
+
+ assertThrows(
+ function(){ eval("function(a, {}){'use strict';}") }, SyntaxError);
+ assertThrows(function(){ eval("(a, {}) => {'use strict';}") }, SyntaxError);
+ assertThrows(
+ function(){ eval("(class{foo(a, {}) {'use strict';}});") }, SyntaxError);
+})();
return args.length; })(1,2,3,4,5));
})();
-function strictTest(a, b, ...c) {
+var strictTest = (function() {
"use strict";
- assertEquals(Array, c.constructor);
- assertTrue(Array.isArray(c));
+ return function strictTest(a, b, ...c) {
+ assertEquals(Array, c.constructor);
+ assertTrue(Array.isArray(c));
- var expectedLength = arguments.length >= 3 ? arguments.length - 2 : 0;
- assertEquals(expectedLength, c.length);
+ var expectedLength = arguments.length >= 3 ? arguments.length - 2 : 0;
+ assertEquals(expectedLength, c.length);
- for (var i = 2, j = 0; i < arguments.length; ++i) {
- assertEquals(c[j++], arguments[i]);
- }
-}
+ for (var i = 2, j = 0; i < arguments.length; ++i) {
+ assertEquals(c[j++], arguments[i]);
+ }
+ };
+})();
function sloppyTest(a, b, ...c) {
assertEquals(Array, c.constructor);
(function testNoAliasArgumentsStrict() {
- function strictF(a, ...rest) {
+ ((function() {
"use strict";
- arguments[0] = 1;
- assertEquals(3, a);
- arguments[1] = 2;
- assertArrayEquals([4, 5], rest);
- }
- strictF(3, 4, 5);
+ return (function strictF(a, ...rest) {
+ arguments[0] = 1;
+ assertEquals(3, a);
+ arguments[1] = 2;
+ assertArrayEquals([4, 5], rest);
+ });
+ })())(3, 4, 5);
})();
assertEquals([1, 2, 3], c.child);
assertEquals([1, 2, 3], c.base);
})();
+
+(function TestDirectiveThrows() {
+ "use strict";
+
+ assertThrows(
+ function(){ eval("function(...rest){'use strict';}") }, SyntaxError);
+ assertThrows(function(){ eval("(...rest) => {'use strict';}") }, SyntaxError);
+ assertThrows(
+ function(){ eval("(class{foo(...rest) {'use strict';}});") }, SyntaxError);
+
+ assertThrows(
+ function(){ eval("function(a, ...rest){'use strict';}") }, SyntaxError);
+ assertThrows(
+ function(){ eval("(a, ...rest) => {'use strict';}") }, SyntaxError);
+ assertThrows(
+ function(){ eval("(class{foo(a, ...rest) {'use strict';}});") },
+ SyntaxError);
+})();
// Flags: --harmony-spreadcalls --harmony-sloppy --harmony-rest-parameters
-(function testCallSuperProperty() {
+(function testCallSuperPropertyStrict() {
+ "use strict";
class BaseClass {
- strict_method(...args) { "use strict"; return [this].concat(args); }
- sloppy_method(...args) { return [this].concat(args); }
+ method(...args) { return [this].concat(args); }
}
class SubClass extends BaseClass {
- strict_m(...args) { return super.strict_method(...args); }
- sloppy_m(...args) { return super.sloppy_method(...args); }
+ method(...args) { return super.method(...args); }
}
var c = new SubClass();
- assertEquals([c, 1, 2, 3, 4, 5], c.strict_m(1, 2, 3, 4, 5));
- assertEquals([c, 1, 2, 3, 4, 5], c.sloppy_m(1, 2, 3, 4, 5));
+ assertEquals([c, 1, 2, 3, 4, 5], c.method(1, 2, 3, 4, 5));
+})();
+
+
+(function testCallSuperPropertySloppy() {
+ class BaseClass {
+ method(...args) { return [this].concat(args); }
+ }
+ class SubClass extends BaseClass {
+ method(...args) { return super.method(...args); }
+ }
+
+ var c = new SubClass();
+ assertEquals([c, 1, 2, 3, 4, 5], c.method(1, 2, 3, 4, 5));
})();
// Flags: --harmony-arrow-functions --strong-mode --allow-natives-syntax
(function() {
- function f({ x = function() { return []; } }) { "use strong"; return x(); }
+ var f = (function() {
+ "use strong";
+ return function f({ x = function() { return []; } }) { return x(); };
+ })();
var a = f({ x: undefined });
assertTrue(%IsStrong(a));
assertFalse(%IsStrong(a));
function outerf() { return []; }
- function f2({ x = outerf }) { "use strong"; return x(); }
+ var f2 = (function() {
+ "use strong";
+ return function f2({ x = outerf }) { return x(); };
+ })();
a = f2({ x: undefined });
assertFalse(%IsStrong(a));
})();
}
-function generateParams(n) {
+function generateParams(n, directive_in_body) {
let a = [];
for (let i = 0; i < n; i++) {
a[i] = `p${i}`;
return a.join(', ');
}
-function generateParamsWithRest(n) {
+function generateParamsWithRest(n, directive_in_body) {
let a = [];
let i = 0;
for (; i < n; i++) {
a[i] = `p${i}`;
}
- a.push(`...p${i}`)
+ if (!directive_in_body) {
+ // If language mode directive occurs in body, rest parameters will trigger
+ // an early error regardless of language mode.
+ a.push(`...p${i}`);
+ }
return a.join(', ');
}
for (let call of calls) {
let code = `'use strict'; ${def}; ${call};`;
if (argumentCount < parameterCount) {
+ print(code);
assertThrows(code, TypeError);
} else {
assertDoesNotThrow(code);
for (let parameterCount = 0; parameterCount < 3; parameterCount++) {
let defs = [
`let o = new class {
- m(${genParams(parameterCount)}) { 'use strong'; }
+ m(${genParams(parameterCount, true)}) { 'use strong'; }
}`,
`let o = new class {
- *m(${genParams(parameterCount)}) { 'use strong'; }
+ *m(${genParams(parameterCount, true)}) { 'use strong'; }
}`,
- `let o = { m(${genParams(parameterCount)}) { 'use strong'; } }`,
- `let o = { *m(${genParams(parameterCount)}) { 'use strong'; } }`,
+ `let o = { m(${genParams(parameterCount, true)}) { 'use strong'; } }`,
+ `let o = { *m(${genParams(parameterCount, true)}) { 'use strong'; } }`,
`'use strong';
let o = new class { m(${genParams(parameterCount)}) {} }`,
`'use strong';
class C { constructor(${genParams(parameterCount)}) {} }`,
`'use strict';
class C {
- constructor(${genParams(parameterCount)}) { 'use strong'; }
+ constructor(${genParams(parameterCount, true)}) { 'use strong'; }
}`,
];
for (let def of defs) {
}`,
`'use strict';
class B {
- constructor(${genParams(parameterCount)}) { 'use strong'; }
+ constructor(${genParams(parameterCount, true)}) { 'use strong'; }
}
class C extends B {
constructor() {
class C extends B {}`,
`'use strict';
class B {
- constructor(${genParams(parameterCount)}) { 'use strong'; }
+ constructor(${genParams(parameterCount, true)}) { 'use strong'; }
}
class C extends B {}`,
];
assertWeakArray({a: [], b: {}}.a);
})();
-(function StrongArrayLiterals(...args) {
+(function StrongArrayLiterals() {
'use strong';
function assertStrongArray(x) {
assertTrue(%IsStrong(x));
assertSame(Array.prototype, Object.getPrototypeOf(x));
}
let [...r] = [];
- assertStrongArray(args);
+ assertStrongArray((function(...a) { return a; })());
assertStrongArray(r);
assertStrongArray([]);
assertStrongArray([1, 2, 3]);