Add field evaluation to source report generation

Change-Id: I62808852a35ef53c05ac1ea715d4559db46bb3d5
Reviewed-on: https://dart-review.googlesource.com/c/89173
Commit-Queue: Zichang Guo <zichangguo@google.com>
Reviewed-by: Siva Annamalai <asiva@google.com>
diff --git a/runtime/observatory/tests/service/get_source_report_test.dart b/runtime/observatory/tests/service/get_source_report_test.dart
index ce2fa5b3..f8dbfaaa 100644
--- a/runtime/observatory/tests/service/get_source_report_test.dart
+++ b/runtime/observatory/tests/service/get_source_report_test.dart
@@ -78,9 +78,9 @@
     final numRanges = coverage['ranges'].length;
     expect(coverage['type'], equals('SourceReport'));
 
-    // Running in app_jitk mode will result in the number of ranges being 6
-    // during the training run and 7 when running from the snapshot.
-    expect(((numRanges == 6) || (numRanges == 7)), isTrue);
+    // Running in app_jitk mode will result in the number of ranges being 7
+    // during the training run and 8 when running from the snapshot.
+    expect(((numRanges == 7) || (numRanges == 8)), isTrue);
     expect(coverage['ranges'][0], equals(expectedRange));
     expect(coverage['scripts'].length, 1);
     expect(
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index 0a1a227..753a7c3 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -2789,17 +2789,11 @@
         if (!field.has_initializer()) {
           continue;
         }
-
-        bool has_func_literal_initializer = false;
-#ifndef DART_PRECOMPILED_RUNTIME
-        has_func_literal_initializer =
-            kernel::FieldHasFunctionLiteralInitializer(field, &start, &end);
-#endif  // !DART_PRECOMPILED_RUNTIME
-        if (has_func_literal_initializer) {
-          if ((start <= token_pos && token_pos <= end) ||
-              (token_pos <= start && start <= last_token_pos)) {
-            return true;
-          }
+        start = field.token_pos();
+        end = field.end_token_pos();
+        if ((start <= token_pos && token_pos <= end) ||
+            (token_pos <= start && start <= last_token_pos)) {
+          return true;
         }
       }
     }
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index da7a69c..f9a3850 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -3036,6 +3036,7 @@
   return true;
 }
 
+#if !defined(DART_PRECOMPILED_RUNTIME)
 static const char* const report_enum_names[] = {
     SourceReport::kCallSitesStr,
     SourceReport::kCoverageStr,
@@ -3043,18 +3044,25 @@
     SourceReport::kProfileStr,
     NULL,
 };
+#endif
 
 static const MethodParameter* get_source_report_params[] = {
+#if !defined(DART_PRECOMPILED_RUNTIME)
     RUNNABLE_ISOLATE_PARAMETER,
     new EnumListParameter("reports", true, report_enum_names),
     new IdParameter("scriptId", false),
     new UIntParameter("tokenPos", false),
     new UIntParameter("endTokenPos", false),
     new BoolParameter("forceCompile", false),
+#endif
     NULL,
 };
 
 static bool GetSourceReport(Thread* thread, JSONStream* js) {
+#if defined(DART_PRECOMPILED_RUNTIME)
+  js->PrintError(kFeatureDisabled, "disabled in AOT mode and PRODUCT.");
+  return false;
+#else
   if (CheckCompilerDisabled(thread, js)) {
     return true;
   }
@@ -3116,6 +3124,7 @@
   report.PrintJSON(js, script, TokenPosition(start_pos),
                    TokenPosition(end_pos));
   return true;
+#endif  // !DART_PRECOMPILED_RUNTIME
 }
 
 static const MethodParameter* reload_sources_params[] = {
diff --git a/runtime/vm/source_report.cc b/runtime/vm/source_report.cc
index 7ce25de..a1e3da2 100644
--- a/runtime/vm/source_report.cc
+++ b/runtime/vm/source_report.cc
@@ -1,11 +1,13 @@
 // Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-#ifndef PRODUCT
+#include "vm/globals.h"
+#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
 #include "vm/source_report.h"
 
 #include "vm/compiler/jit/compiler.h"
 #include "vm/isolate.h"
+#include "vm/kernel_loader.h"
 #include "vm/object.h"
 #include "vm/object_store.h"
 #include "vm/profiler.h"
@@ -90,6 +92,7 @@
     case RawFunction::kRegularFunction:
     case RawFunction::kClosureFunction:
     case RawFunction::kImplicitClosureFunction:
+    case RawFunction::kImplicitStaticFinalGetter:
     case RawFunction::kGetterFunction:
     case RawFunction::kSetterFunction:
     case RawFunction::kConstructor:
@@ -111,6 +114,28 @@
   return false;
 }
 
+bool SourceReport::ShouldSkipField(const Field& field) {
+  if (!field.token_pos().IsReal() || !field.end_token_pos().IsReal()) {
+    // At least one of the token positions is not known.
+    return true;
+  }
+
+  if (script_ != NULL && !script_->IsNull()) {
+    if (field.Script() != script_->raw()) {
+      // The field is from the wrong script.
+      return true;
+    }
+    if (((start_pos_ > TokenPosition::kMinSource) &&
+         (field.end_token_pos() < start_pos_)) ||
+        ((end_pos_ > TokenPosition::kMinSource) &&
+         (field.token_pos() > end_pos_))) {
+      // The field does not intersect with the requested token range.
+      return true;
+    }
+  }
+  return false;
+}
+
 intptr_t SourceReport::GetScriptIndex(const Script& script) {
   ScriptTableEntry wrapper;
   const String& url = String::Handle(zone(), script.url());
@@ -465,10 +490,25 @@
   }
 }
 
+void SourceReport::VisitField(JSONArray* jsarr, const Field& field) {
+  if (ShouldSkipField(field) || !field.has_initializer()) return;
+  const Function& func =
+      Function::Handle(zone(), GetInitializerFunction(field));
+  VisitFunction(jsarr, func);
+}
+
+RawFunction* SourceReport::GetInitializerFunction(const Field& field) {
+  Thread* const thread = Thread::Current();
+  // Create a function to evaluate the initializer
+  return kernel::CreateFieldInitializerFunction(thread, thread->zone(), field);
+}
+
 void SourceReport::VisitLibrary(JSONArray* jsarr, const Library& lib) {
   Class& cls = Class::Handle(zone());
   Array& functions = Array::Handle(zone());
+  Array& fields = Array::Handle(zone());
   Function& func = Function::Handle(zone());
+  Field& field = Field::Handle(zone());
   Script& script = Script::Handle(zone());
   ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate);
   while (it.HasNext()) {
@@ -505,6 +545,12 @@
       func ^= functions.At(i);
       VisitFunction(jsarr, func);
     }
+
+    fields = cls.fields();
+    for (intptr_t i = 0; i < fields.Length(); i++) {
+      field ^= fields.At(i);
+      VisitField(jsarr, field);
+    }
   }
 }
 
@@ -554,4 +600,4 @@
 }
 
 }  // namespace dart
-#endif  // PRODUCT
+#endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
diff --git a/runtime/vm/source_report.h b/runtime/vm/source_report.h
index 5ab147f..73a9c3d 100644
--- a/runtime/vm/source_report.h
+++ b/runtime/vm/source_report.h
@@ -5,6 +5,9 @@
 #ifndef RUNTIME_VM_SOURCE_REPORT_H_
 #define RUNTIME_VM_SOURCE_REPORT_H_
 
+#include "vm/globals.h"
+#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
+
 #include "vm/allocation.h"
 #include "vm/flags.h"
 #include "vm/hash_map.h"
@@ -60,6 +63,7 @@
 
   bool IsReportRequested(ReportKind report_kind);
   bool ShouldSkipFunction(const Function& func);
+  bool ShouldSkipField(const Field& field);
   intptr_t GetScriptIndex(const Script& script);
   bool ScriptIsLoadedByLibrary(const Script& script, const Library& lib);
 
@@ -79,9 +83,10 @@
   void PrintScriptTable(JSONArray* jsarr);
 
   void VisitFunction(JSONArray* jsarr, const Function& func);
+  void VisitField(JSONArray* jsarr, const Field& field);
   void VisitLibrary(JSONArray* jsarr, const Library& lib);
   void VisitClosures(JSONArray* jsarr);
-
+  RawFunction* GetInitializerFunction(const Field& field);
   // An entry in the script table.
   struct ScriptTableEntry {
     ScriptTableEntry() : key(NULL), index(-1), script(NULL) {}
@@ -122,4 +127,5 @@
 
 }  // namespace dart
 
+#endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
 #endif  // RUNTIME_VM_SOURCE_REPORT_H_