Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(747)

Unified Diff: runtime/vm/parser_test.cc

Issue 267583002: Add some testing for the variable allocation code in the Parser. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: improve tests Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/vm/object.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/vm/parser_test.cc
diff --git a/runtime/vm/parser_test.cc b/runtime/vm/parser_test.cc
index c594fcb70f80db0d2315ac55d8195222fdd3239e..dbbb85bb15e8325661963de57414aada4d378fc2 100644
--- a/runtime/vm/parser_test.cc
+++ b/runtime/vm/parser_test.cc
@@ -5,6 +5,7 @@
#include "vm/ast_printer.h"
#include "vm/class_finalizer.h"
+#include "vm/debugger.h"
#include "vm/longjump.h"
#include "vm/object.h"
#include "vm/parser.h"
@@ -13,6 +14,8 @@
namespace dart {
+DECLARE_FLAG(bool, show_invisible_frames);
+
void DumpFunction(const Library& lib, const char* cname, const char* fname) {
const String& classname = String::Handle(Symbols::New(cname));
@@ -162,4 +165,296 @@ TEST_CASE(Parser_TopLevel) {
DumpFunction(lib, "B", "bam");
}
+
+const char* saved_vars = NULL;
+
+
+// Saves the var descriptors for all frames on the stack as a string.
+static void SaveVars(Dart_IsolateId isolate_id,
+ intptr_t bp_id,
+ const Dart_CodeLocation& loc) {
+ DebuggerStackTrace* stack =
+ Isolate::Current()->debugger()->StackTrace();
+ intptr_t num_frames = stack->Length();
+ const int kBufferLen = 2048;
+ char* buffer = new char[kBufferLen];
+ char* pos = buffer;
+ LocalVarDescriptors& var_desc = LocalVarDescriptors::Handle();
+ for (intptr_t i = 0; i < num_frames; i++) {
+ ActivationFrame* frame = stack->FrameAt(i);
+ var_desc = frame->code().var_descriptors();
+ pos += OS::SNPrint(pos, (kBufferLen - (pos - buffer)),
+ "%s\n%s",
+ frame->function().ToQualifiedCString(),
+ var_desc.ToCString());
+ }
+ pos[0] = '\0';
+ saved_vars = buffer;
+}
+
+
+// Uses the debugger to pause the program and capture the variable
+// descriptors for all frames on the stack.
+static const char* CaptureVarsAtLine(Dart_Handle lib,
+ const char* entry,
+ int line) {
+ EXPECT(ClassFinalizer::ProcessPendingClasses());
+ bool saved_flag = FLAG_show_invisible_frames;
+ FLAG_show_invisible_frames = true;
+ Isolate* isolate = Isolate::Current();
+ Debugger* debugger = isolate->debugger();
+ const String& url = String::Handle(String::New(TestCase::url()));
+ Dart_SetPausedEventHandler(SaveVars);
+ debugger->SetBreakpointAtLine(url, line);
+ saved_vars = NULL;
+ EXPECT_VALID(Dart_Invoke(lib, NewString(entry), 0, NULL));
+ const char* tmp = saved_vars;
+ saved_vars = NULL;
+ FLAG_show_invisible_frames = saved_flag;
+ return tmp;
+}
+
+
+TEST_CASE(Parser_AllocateVariables_CapturedVar) {
+ const char* kScriptChars =
+ "int main() {\n"
+ " var value = 11;\n"
+ " int f(var param) {\n"
+ " return param + value;\n" // line 4
+ " }\n"
+ " return f(22);\n"
+ "}\n";
+ Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
+ EXPECT_VALID(lib);
+ EXPECT_STREQ(
+ // function f uses one ctx var at (0,0); doesn't save ctx.
+ "::.main_f\n"
+ " 0 ContextVar level=0 index=0 begin=14 end=28 name=value\n"
+ " 1 StackVar scope=1 index=2 begin=16 end=28 name=param\n"
+
+ // Closure call saves current context.
+ "(dynamic, dynamic) => int.call\n"
+ " 0 StackVar scope=1 index=3 begin=0 end=0 name=this\n"
+ " 1 SavedCurrentCtx scope=0 index=-3 begin=0 end=0"
+ " name=:saved_current_context_var\n"
+
+ // function main uses one ctx var at (1,0); saves caller ctx.
+ "::.main\n"
+ " 0 ContextLevel level=1 scope=1 begin=2 end=37\n"
+ " 1 SavedEntryCtx scope=0 index=-4 begin=0 end=0"
+ " name=:saved_entry_context_var\n"
+ " 2 ContextVar level=1 index=0 begin=7 end=37 name=value\n"
+ " 3 StackVar scope=2 index=-3 begin=12 end=37 name=f\n",
+ CaptureVarsAtLine(lib, "main", 4));
+}
+
+
+TEST_CASE(Parser_AllocateVariables_NestedCapturedVar) {
+ const char* kScriptChars =
+ "int a() {\n"
+ " int b() {\n"
+ " var value = 11;\n"
+ " int c() {\n"
+ " return value;\n" // line 5
+ " }\n"
+ " return c();\n"
+ " }\n"
+ " return b();\n"
+ "}\n";
+ Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
+ EXPECT_VALID(lib);
+ EXPECT_STREQ(
+ // Innermost function uses captured variable 'value' from middle
+ // function.
+ "::.a_b_c\n"
+ " 0 ContextVar level=0 index=0 begin=20 end=30 name=value\n"
+
+ // Closure call saves current context.
+ "(dynamic) => int.call\n"
+ " 0 StackVar scope=1 index=2 begin=0 end=0 name=this\n"
+ " 1 SavedCurrentCtx scope=0 index=-3 begin=0 end=0"
+ " name=:saved_current_context_var\n"
+
+ // Middle function saves the entry context. Notice that this
+ // happens here and not in the outermost function. We always
+ // save the entry context at the last possible moment.
+ "::.a_b\n"
+ " 0 ContextLevel level=1 scope=1 begin=8 end=38\n"
+ " 1 SavedEntryCtx scope=0 index=-4 begin=0 end=0"
+ " name=:saved_entry_context_var\n"
+ " 2 ContextVar level=1 index=0 begin=13 end=38 name=value\n"
+ " 3 StackVar scope=2 index=-3 begin=18 end=38 name=c\n"
+
+ // Closure call saves current context.
+ "(dynamic) => int.call\n"
+ " 0 StackVar scope=1 index=2 begin=0 end=0 name=this\n"
+ " 1 SavedCurrentCtx scope=0 index=-3 begin=0 end=0"
+ " name=:saved_current_context_var\n"
+
+ // Outermost function neglects to save the entry context. We
+ // don't save the entry context if the function has no captured
+ // variables.
+ "::.a\n"
+ " 0 StackVar scope=2 index=-3 begin=6 end=46 name=b\n",
+ CaptureVarsAtLine(lib, "a", 5));
+}
+
+
+TEST_CASE(Parser_AllocateVariables_TwoChains) {
+ const char* kScriptChars =
+ "int a() {\n"
+ " var value1 = 11;\n"
+ " int b() {\n"
+ " int aa() {\n"
+ " var value2 = 12;\n"
+ " int bb() {\n"
+ " return value2;\n" // line 7
+ " }\n"
+ " return bb();\n"
+ " }\n"
+ " return value1 + aa();\n"
+ " }\n"
+ " return b();\n"
+ "}\n";
+ Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
+ EXPECT_VALID(lib);
+ EXPECT_STREQ(
+ // bb captures only value2 from aa. No others.
+ "::.a_b_aa_bb\n"
+ " 0 ContextVar level=0 index=0 begin=32 end=42 name=value2\n"
+
+ // Closure call saves current context.
+ "(dynamic) => int.call\n"
+ " 0 StackVar scope=1 index=2 begin=0 end=0 name=this\n"
+ " 1 SavedCurrentCtx scope=0 index=-3 begin=0 end=0"
+ " name=:saved_current_context_var\n"
+
+ // aa shares value2. Notice that we save the entry ctx instead
+ // of chaining from b. This keeps us from holding onto closures
+ // that we would never access.
+ "::.a_b_aa\n"
+ " 0 ContextLevel level=1 scope=1 begin=20 end=50\n"
+ " 1 SavedEntryCtx scope=0 index=-4 begin=0 end=0"
+ " name=:saved_entry_context_var\n"
+ " 2 ContextVar level=1 index=0 begin=25 end=50 name=value2\n"
+ " 3 StackVar scope=2 index=-3 begin=30 end=50 name=bb\n"
+
+ // Closure call saves current context.
+ "(dynamic) => int.call\n"
+ " 0 StackVar scope=1 index=2 begin=0 end=0 name=this\n"
+ " 1 SavedCurrentCtx scope=0 index=-3 begin=0 end=0"
+ " name=:saved_current_context_var\n"
+
+ // b captures value1 from a.
+ "::.a_b\n"
+ " 0 ContextVar level=0 index=0 begin=14 end=60 name=value1\n"
+ " 1 StackVar scope=2 index=-3 begin=18 end=60 name=aa\n"
+
+ // Closure call saves current context.
+ "(dynamic) => int.call\n"
+ " 0 StackVar scope=1 index=2 begin=0 end=0 name=this\n"
+ " 1 SavedCurrentCtx scope=0 index=-3 begin=0 end=0"
+ " name=:saved_current_context_var\n"
+
+ // a shares value1, saves entry ctx.
+ "::.a\n"
+ " 0 ContextLevel level=1 scope=1 begin=2 end=68\n"
+ " 1 SavedEntryCtx scope=0 index=-4 begin=0 end=0"
+ " name=:saved_entry_context_var\n"
+ " 2 ContextVar level=1 index=0 begin=7 end=68 name=value1\n"
+ " 3 StackVar scope=2 index=-3 begin=12 end=68 name=b\n",
+ CaptureVarsAtLine(lib, "a", 7));
+}
+
+
+TEST_CASE(Parser_AllocateVariables_Issue7681) {
+ // This is a distilled version of the program from Issue 7681.
+ //
+ // When we create the closure at line 11, we need to make sure to
+ // save the entry context instead of chaining to the parent context.
+ //
+ // This test is somewhat redundant with CapturedVarChain but
+ // included for good measure.
+ const char* kScriptChars =
+ "class X {\n"
+ " Function onX;\n"
+ "}\n"
+ "\n"
+ "class Y {\n"
+ " Function onY;\n"
+ "}\n"
+ "\n"
+ "void doIt() {\n"
+ " var x = new X();\n"
+ " x.onX = (y) {\n"
+ " y.onY = () {\n" // line 12
+ " return y;\n"
+ " };\n"
+ " };\n"
+ " x.onX(new Y());\n"
+ "}\n";
+ Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
+ EXPECT_VALID(lib);
+ EXPECT_STREQ(
+ // This frame saves the entry context instead of chaining. Good.
+ "::.doIt_<anonymous closure>\n"
+ " 0 ContextLevel level=1 scope=1 begin=41 end=62\n"
+ " 1 ContextVar level=1 index=0 begin=42 end=62 name=y\n"
+ " 2 SavedEntryCtx scope=0 index=-3 begin=0 end=0"
+ " name=:saved_entry_context_var\n"
+
+ // Closure call saves current context.
+ "(dynamic, dynamic) => dynamic.call\n"
+ " 0 StackVar scope=1 index=3 begin=0 end=0 name=this\n"
+ " 1 SavedCurrentCtx scope=0 index=-3 begin=0 end=0"
+ " name=:saved_current_context_var\n"
+
+ "X.onX\n"
+ " 0 StackVar scope=1 index=3 begin=0 end=0 name=this\n"
+
+ // No context is saved here since no vars are captured.
+ "::.doIt\n"
+ " 0 StackVar scope=2 index=-3 begin=29 end=77 name=x\n",
+ CaptureVarsAtLine(lib, "doIt", 12));
+}
+
+
+TEST_CASE(Parser_AllocateVariables_CaptureLoopVar) {
+ const char* kScriptChars =
+ "int outer() {\n"
+ " for(int i = 0; i < 1; i++) {\n"
+ " var value = 11 + i;\n"
+ " int inner() {\n"
+ " return value;\n" // line 5
+ " }\n"
+ " return inner();\n"
+ " }\n"
+ "}\n";
+ Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
+ EXPECT_VALID(lib);
+ EXPECT_STREQ(
+ // inner function captures variable value. That's fine.
+ "::.outer_inner\n"
+ " 0 ContextVar level=0 index=0 begin=32 end=42 name=value\n"
+
+ // Closure call saves current context.
+ "(dynamic) => int.call\n"
+ " 0 StackVar scope=1 index=2 begin=0 end=0 name=this\n"
+ " 1 SavedCurrentCtx scope=0 index=-3 begin=0 end=0"
+ " name=:saved_current_context_var\n"
+
+ // Notice that the outer function neglects to save the entry
+ // context. This is a bug.
+ //
+ // TODO(turnidge): Fix this very soon and update this test.
+ //
+ // https://code.google.com/p/dart/issues/detail?id=18561
+ "::.outer\n"
+ " 0 StackVar scope=3 index=-3 begin=9 end=50 name=i\n"
+ " 1 ContextLevel level=1 scope=4 begin=20 end=50\n"
+ " 2 ContextVar level=1 index=0 begin=23 end=50 name=value\n"
+ " 3 StackVar scope=4 index=-4 begin=30 end=50 name=inner\n",
+ CaptureVarsAtLine(lib, "outer", 5));
+}
+
} // namespace dart
« no previous file with comments | « runtime/vm/object.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698