Detect circular dependencies in compile time constants

Fix issue 1681

R=srdjan@google.com

Review URL: https://codereview.chromium.org//22897019

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@26491 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/runtime/vm/object_store.cc b/runtime/vm/object_store.cc
index e53b5d9..a4b7ec6 100644
--- a/runtime/vm/object_store.cc
+++ b/runtime/vm/object_store.cc
@@ -63,6 +63,7 @@
     utf_library_(Library::null()),
     libraries_(GrowableObjectArray::null()),
     pending_classes_(GrowableObjectArray::null()),
+    pending_functions_(GrowableObjectArray::null()),
     sticky_error_(Error::null()),
     unhandled_exception_handler_(String::null()),
     empty_context_(Context::null()),
@@ -105,6 +106,9 @@
   ASSERT(this->out_of_memory() == Instance::null());
   ASSERT(this->preallocated_stack_trace() == Stacktrace::null());
 
+  ASSERT(this->pending_functions() == GrowableObjectArray::null());
+  this->pending_functions_ = GrowableObjectArray::New();
+
   Object& result = Object::Handle();
   const Library& library = Library::Handle(Library::CoreLibrary());
 
diff --git a/runtime/vm/object_store.h b/runtime/vm/object_store.h
index 48e711a..6ccdff9 100644
--- a/runtime/vm/object_store.h
+++ b/runtime/vm/object_store.h
@@ -349,6 +349,10 @@
     pending_classes_ = value.raw();
   }
 
+  RawGrowableObjectArray* pending_functions() const {
+    return pending_functions_;
+  }
+
   RawError* sticky_error() const { return sticky_error_; }
   void set_sticky_error(const Error& value) {
     ASSERT(!value.IsNull());
@@ -485,6 +489,7 @@
   RawLibrary* utf_library_;
   RawGrowableObjectArray* libraries_;
   RawGrowableObjectArray* pending_classes_;
+  RawGrowableObjectArray* pending_functions_;
   RawError* sticky_error_;
   RawString* unhandled_exception_handler_;
   RawContext* empty_context_;
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index 89d7731..1e96917 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -255,7 +255,8 @@
       current_class_(Class::Handle(isolate_)),
       library_(Library::Handle(isolate_, library.raw())),
       try_blocks_list_(NULL),
-      last_used_try_index_(CatchClauseNode::kInvalidTryIndex) {
+      last_used_try_index_(CatchClauseNode::kInvalidTryIndex),
+      unregister_pending_function_(false) {
   ASSERT(tokens_iterator_.IsValid());
   ASSERT(!library.IsNull());
 }
@@ -284,7 +285,8 @@
           isolate_,
           parsed_function->function().origin()).library())),
       try_blocks_list_(NULL),
-      last_used_try_index_(CatchClauseNode::kInvalidTryIndex) {
+      last_used_try_index_(CatchClauseNode::kInvalidTryIndex),
+      unregister_pending_function_(false) {
   ASSERT(tokens_iterator_.IsValid());
   ASSERT(!current_function().IsNull());
   if (FLAG_enable_type_checks) {
@@ -293,6 +295,19 @@
 }
 
 
+Parser::~Parser() {
+  if (unregister_pending_function_) {
+    const GrowableObjectArray& pending_functions =
+        GrowableObjectArray::Handle(
+            isolate()->object_store()->pending_functions());
+    ASSERT(pending_functions.Length() > 0);
+    ASSERT(pending_functions.At(pending_functions.Length()-1) ==
+        current_function().raw());
+    pending_functions.RemoveLast();
+  }
+}
+
+
 void Parser::SetScript(const Script & script, intptr_t token_pos) {
   script_ = script.raw();
   tokens_iterator_.SetStream(TokenStream::Handle(script.tokens()), token_pos);
@@ -2360,6 +2375,23 @@
 }
 
 
+void Parser::CheckRecursiveInvocation() {
+  const GrowableObjectArray& pending_functions =
+      GrowableObjectArray::Handle(
+          isolate()->object_store()->pending_functions());
+  for (int i = 0; i < pending_functions.Length(); i++) {
+    if (pending_functions.At(i) == current_function().raw()) {
+      const String& fname =
+          String::Handle(current_function().UserVisibleName());
+      ErrorMsg("circular dependency for function %s", fname.ToCString());
+    }
+  }
+  ASSERT(!unregister_pending_function_);
+  pending_functions.Add(current_function());
+  unregister_pending_function_ = true;
+}
+
+
 // Parser is at the opening parenthesis of the formal parameter declaration
 // of function. Parse the formal parameters, initializers and code.
 SequenceNode* Parser::ParseConstructor(const Function& func,
@@ -2372,6 +2404,8 @@
   const Class& cls = Class::Handle(func.Owner());
   ASSERT(!cls.IsNull());
 
+  CheckRecursiveInvocation();
+
   if (func.IsImplicitConstructor()) {
     // Special case: implicit constructor.
     // The parser adds an implicit default constructor when a class
diff --git a/runtime/vm/parser.h b/runtime/vm/parser.h
index 2af08c2..b2fbf97 100644
--- a/runtime/vm/parser.h
+++ b/runtime/vm/parser.h
@@ -182,6 +182,7 @@
 
   Parser(const Script& script, const Library& library, intptr_t token_pos);
   Parser(const Script& script, ParsedFunction* function, intptr_t token_pos);
+  ~Parser();
 
   // The function for which we will generate code.
   const Function& current_function() const;
@@ -320,6 +321,8 @@
       const Error& prev_error, intptr_t token_pos, const char* format, ...)
       PRINTF_ATTRIBUTE(4, 5);
 
+  void CheckRecursiveInvocation();
+
   const Instance& EvaluateConstExpr(AstNode* expr);
   AstNode* RunStaticFieldInitializer(const Field& field);
   RawObject* EvaluateConstConstructorCall(
@@ -693,6 +696,8 @@
   intptr_t AllocateTryIndex() { return ++last_used_try_index_; }
   intptr_t last_used_try_index_;
 
+  bool unregister_pending_function_;
+
   DISALLOW_COPY_AND_ASSIGN(Parser);
 };
 
diff --git a/tests/co19/co19-runtime.status b/tests/co19/co19-runtime.status
index 102fbbf..c52aec6 100644
--- a/tests/co19/co19-runtime.status
+++ b/tests/co19/co19-runtime.status
@@ -138,7 +138,6 @@
 Language/12_Expressions/01_Constants_A01_t01: fail # co19 issue 522
 Language/12_Expressions/01_Constants_A16_t01: fail # co19 issue 525
 Language/12_Expressions/01_Constants_A16_t02: fail # co19 issue 525
-Language/12_Expressions/01_Constants_A17_t03: crash # Dart issue 1681
 Language/12_Expressions/03_Numbers_A01_t01: fail # co19 issue 522
 Language/12_Expressions/03_Numbers_A01_t02: fail # co19 issue 522
 Language/12_Expressions/03_Numbers_A01_t03: fail # co19 issue 522