// 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 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"
#include "vm/object.h"
#include "vm/profiler_service.h"
#include "vm/token_position.h"

namespace dart {

// A SourceReport object is used to generate reports about the program
// source code, with information associated with source token
// positions.  There are multiple possible kinds of reports.
class SourceReport {
 public:
  enum ReportKind {
    kCallSites = 0x1,
    kCoverage = 0x2,
    kPossibleBreakpoints = 0x4,
    kProfile = 0x8,
    kBranchCoverage = 0x10,
  };

  static const char* kCallSitesStr;
  static const char* kCoverageStr;
  static const char* kPossibleBreakpointsStr;
  static const char* kProfileStr;
  static const char* kBranchCoverageStr;

  enum CompileMode { kNoCompile, kForceCompile };

  // report_set is a bitvector indicating which reports to generate
  // (e.g. kCallSites | kCoverage).
  //
  // If library_filters is not null, then the report will only include libraries
  // whose URIs start with one of the filter strings.
  explicit SourceReport(intptr_t report_set,
                        CompileMode compile = kNoCompile,
                        bool report_lines = false);
  explicit SourceReport(intptr_t report_set,
                        const GrowableObjectArray& library_filters,
                        ZoneCStringSet* libraries_already_compiled = nullptr,
                        CompileMode compile = kNoCompile,
                        bool report_lines = false);
  ~SourceReport();

  // Generate a source report for (some subrange of) a script.
  //
  // If script is null, then the report is generated for all scripts
  // in the isolate.
  void PrintJSON(JSONStream* js,
                 const Script& script,
                 TokenPosition start_pos = TokenPosition::kMinSource,
                 TokenPosition end_pos = TokenPosition::kMaxSource);

 private:
  void ClearScriptTable();
  void Init(Thread* thread,
            const Script* script,
            TokenPosition start_pos,
            TokenPosition end_pos);

  Thread* thread() const { return thread_; }
  Zone* zone() const { return thread_->zone(); }
  Isolate* isolate() const { return thread_->isolate(); }

  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);
  intptr_t GetTokenPosOrLine(const Script& script,
                             const TokenPosition& token_pos);
  bool IsLibraryAlreadyCompiled(const Library& lib);
  bool ShouldFiltersIncludeScript(const Script& script);
  bool ShouldFiltersIncludeUrl(const String& url);

  void PrintCallSitesData(JSONObject* jsobj,
                          const Function& func,
                          const Code& code);
  void PrintCoverageData(JSONObject* jsobj,
                         const Script& script,
                         intptr_t script_index,
                         const Function& func,
                         bool report_branch_coverage);
  void PrintPossibleBreakpointsData(JSONObject* jsobj,
                                    const Script& script,
                                    const Function& func,
                                    const Code& code);
  void PrintProfileData(JSONObject* jsobj, ProfileFunction* profile_function);
#if defined(DEBUG)
  void VerifyScriptTable();
#endif
  void PrintScriptTable(JSONArray* jsarr);

  void VisitFunction(JSONArray* jsarr,
                     const Function& func,
                     CompileMode compile_mode);
  void VisitCodeOrBytecode(JSONObject* jsobj,
                           const Script& script,
                           intptr_t script_index,
                           const Function& func,
                           const Code& code,
                           CompileMode compile_mode);
  void VisitField(JSONArray* jsarr,
                  const Field& field,
                  CompileMode compile_mode);
  void VisitLibrary(JSONArray* jsarr, const Library& lib);
  void VisitClosures(JSONArray* jsarr);

  // An entry in the script table.
  struct ScriptTableEntry {
    ScriptTableEntry() : key(nullptr), index(-1), script(nullptr) {}

    const String* key;
    intptr_t index;
    const Script* script;
    GrowableArray<TokenPosition> const_constructor_hits;
  };

  // Needed for DirectChainedHashMap.
  struct ScriptTableTrait {
    typedef ScriptTableEntry* Value;
    typedef const ScriptTableEntry* Key;
    typedef ScriptTableEntry* Pair;

    static Key KeyOf(Pair kv) { return kv; }

    static Value ValueOf(Pair kv) { return kv; }

    static inline uword Hash(Key key) { return key->key->Hash(); }

    static inline bool IsKeyEqual(Pair kv, Key key) {
      return kv->script->ptr() == key->script->ptr();
    }
  };

  void CollectAllScripts(
      DirectChainedHashMap<ScriptTableTrait>* local_script_table,
      GrowableArray<ScriptTableEntry*>* local_script_table_entries);

  void CleanupCollectedScripts(
      DirectChainedHashMap<ScriptTableTrait>* local_script_table,
      GrowableArray<ScriptTableEntry*>* local_script_table_entries);

  void CollectConstConstructorCoverageFromScripts(
      GrowableArray<ScriptTableEntry*>* local_script_table_entries);

  intptr_t report_set_;
  CompileMode compile_mode_;
  bool report_lines_;
  const GrowableObjectArray& library_filters_;
  ZoneCStringSet* libraries_already_compiled_;
  Thread* thread_;
  const Script* script_;
  TokenPosition start_pos_;
  TokenPosition end_pos_;
  Profile profile_;
  GrowableArray<ScriptTableEntry*> script_table_entries_;
  DirectChainedHashMap<ScriptTableTrait> script_table_;
  intptr_t next_script_index_;
  intptr_t late_error_class_id_ = ClassId::kIllegalCid;
};

}  // namespace dart

#endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
#endif  // RUNTIME_VM_SOURCE_REPORT_H_
