Index: test/cctest/test-parsing.cc |
diff --git a/test/cctest/test-parsing.cc b/test/cctest/test-parsing.cc |
index 550ac17a060eccc728c6f92cc5c151475e55b4bc..96d34211b7a6392365f7753cb33387a1925a94c7 100644 |
--- a/test/cctest/test-parsing.cc |
+++ b/test/cctest/test-parsing.cc |
@@ -37,6 +37,7 @@ |
#include "src/objects.h" |
#include "src/parser.h" |
#include "src/preparser.h" |
+#include "src/rewriter.h" |
#include "src/scanner-character-streams.h" |
#include "src/token.h" |
#include "src/utils.h" |
@@ -2549,3 +2550,131 @@ TEST(FuncNameInferrerEscaped) { |
i::DeleteArray(two_byte_source); |
i::DeleteArray(two_byte_name); |
} |
+ |
+ |
+TEST(InnerAssignment) { |
+ i::Isolate* isolate = CcTest::i_isolate(); |
+ i::Factory* factory = isolate->factory(); |
+ i::HandleScope scope(isolate); |
+ LocalContext env; |
+ |
+ const char* prefix = "function f() {"; |
+ const char* midfix = " function g() {"; |
+ const char* suffix = "}}"; |
+ struct { const char* source; bool assigned; bool strict; } outers[] = { |
+ // Actual assignments. |
+ { "var x; var x = 5;", true, false }, |
+ { "var x; { var x = 5; }", true, false }, |
+ { "'use strict'; let x; x = 6;", true, true }, |
+ // Actual non-assignments. |
+ { "var x;", false, false }, |
+ { "var x = 5;", false, false }, |
+ { "'use strict'; let x;", false, true }, |
+ { "'use strict'; let x = 6;", false, true }, |
+ { "'use strict'; var x = 0; { let x = 6; }", false, true }, |
+ { "'use strict'; var x = 0; { let x; x = 6; }", false, true }, |
+ { "'use strict'; let x = 0; { let x = 6; }", false, true }, |
+ { "'use strict'; let x = 0; { let x; x = 6; }", false, true }, |
+ { "var x; try {} catch (x) { x = 5; }", false, false }, |
+ { "function x() {}", false, false }, |
+ // Eval approximation. |
+ { "var x; eval('');", true, false }, |
+ { "eval(''); var x;", true, false }, |
+ { "'use strict'; let x; eval('');", true, true }, |
+ { "'use strict'; eval(''); let x;", true, true }, |
+ // Non-assignments not recognized, because the analysis is approximative. |
+ { "var x; var x;", true, false }, |
+ { "var x = 5; var x;", true, false }, |
+ { "var x; { var x; }", true, false }, |
+ { "var x; function x() {}", true, false }, |
+ { "function x() {}; var x;", true, false }, |
+ { "var x; try {} catch (x) { var x = 5; }", true, false }, |
+ }; |
+ struct { const char* source; bool assigned; bool with; } inners[] = { |
+ // Actual assignments. |
+ { "x = 1;", true, false }, |
+ { "x++;", true, false }, |
+ { "++x;", true, false }, |
+ { "x--;", true, false }, |
+ { "--x;", true, false }, |
+ { "{ x = 1; }", true, false }, |
+ { "'use strict'; { let x; }; x = 0;", true, false }, |
+ { "'use strict'; { const x = 1; }; x = 0;", true, false }, |
+ { "'use strict'; { function x() {} }; x = 0;", true, false }, |
+ { "with ({}) { x = 1; }", true, true }, |
+ { "eval('');", true, false }, |
+ { "'use strict'; { let y; eval('') }", true, false }, |
+ { "function h() { x = 0; }", true, false }, |
+ { "(function() { x = 0; })", true, false }, |
+ { "(function() { x = 0; })", true, false }, |
+ { "with ({}) (function() { x = 0; })", true, true }, |
+ // Actual non-assignments. |
+ { "", false, false }, |
+ { "x;", false, false }, |
+ { "var x;", false, false }, |
+ { "var x = 8;", false, false }, |
+ { "var x; x = 8;", false, false }, |
+ { "'use strict'; let x;", false, false }, |
+ { "'use strict'; let x = 8;", false, false }, |
+ { "'use strict'; let x; x = 8;", false, false }, |
+ { "'use strict'; const x = 8;", false, false }, |
+ { "function x() {}", false, false }, |
+ { "function x() { x = 0; }", false, false }, |
+ { "function h(x) { x = 0; }", false, false }, |
+ { "'use strict'; { let x; x = 0; }", false, false }, |
+ { "{ var x; }; x = 0;", false, false }, |
+ { "with ({}) {}", false, true }, |
+ { "var x; { with ({}) { x = 1; } }", false, true }, |
+ { "try {} catch(x) { x = 0; }", false, false }, |
+ { "try {} catch(x) { with ({}) { x = 1; } }", false, true }, |
+ // Eval approximation. |
+ { "eval('');", true, false }, |
+ { "function h() { eval(''); }", true, false }, |
+ { "(function() { eval(''); })", true, false }, |
+ // Shadowing not recognized because of eval approximation. |
+ { "var x; eval('');", true, false }, |
+ { "'use strict'; let x; eval('');", true, false }, |
+ { "try {} catch(x) { eval(''); }", true, false }, |
+ { "function x() { eval(''); }", true, false }, |
+ { "(function(x) { eval(''); })", true, false }, |
+ }; |
+ i::Handle<i::String> var_name = factory->InternalizeUtf8String("x"); |
+ |
+ int prefix_len = Utf8LengthHelper(prefix); |
+ int midfix_len = Utf8LengthHelper(midfix); |
+ int suffix_len = Utf8LengthHelper(suffix); |
+ for (unsigned i = 0; i < ARRAY_SIZE(outers); ++i) { |
+ const char* outer = outers[i].source; |
+ int outer_len = Utf8LengthHelper(outer); |
+ for (unsigned j = 0; j < ARRAY_SIZE(inners); ++j) { |
+ if (outers[i].strict && inners[j].with) continue; |
+ const char* inner = inners[j].source; |
+ int inner_len = Utf8LengthHelper(inner); |
+ int len = prefix_len + outer_len + midfix_len + inner_len + suffix_len; |
+ i::ScopedVector<char> program(len + 1); |
+ i::SNPrintF(program, "%s%s%s%s%s", prefix, outer, midfix, inner, suffix); |
+ i::Handle<i::String> source = |
+ factory->InternalizeUtf8String(program.start()); |
+ source->PrintOn(stdout); |
+ printf("\n"); |
+ |
+ i::Handle<i::Script> script = factory->NewScript(source); |
+ i::CompilationInfoWithZone info(script); |
+ i::Parser parser(&info); |
+ parser.set_allow_harmony_scoping(true); |
+ CHECK(parser.Parse()); |
+ CHECK(i::Rewriter::Rewrite(&info)); |
+ CHECK(i::Scope::Analyze(&info)); |
+ CHECK(info.function() != NULL); |
+ |
+ i::Scope* scope = info.function()->scope(); |
+ CHECK_EQ(scope->inner_scopes()->length(), 1); |
+ i::Scope* inner_scope = scope->inner_scopes()->at(0); |
+ i::Variable* var = inner_scope->Lookup(var_name); |
+ bool expected = outers[i].assigned || inners[j].assigned; |
+ CHECK(var != NULL); |
+ CHECK(var->is_used() || !expected); |
+ CHECK(var->maybe_assigned() == expected); |
+ } |
+ } |
+} |