// Copyright (c) 2012, 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_FLAGS_H_
#define RUNTIME_VM_FLAGS_H_

#include "platform/address_sanitizer.h"
#include "platform/assert.h"
#include "platform/memory_sanitizer.h"
#include "platform/thread_sanitizer.h"
#include "vm/flag_list.h"
#include "vm/globals.h"

typedef const char* charp;

#define DECLARE_FLAG(type, name) extern type FLAG_##name

#define DEFINE_FLAG(type, name, default_value, comment)                        \
  type FLAG_##name =                                                           \
      Flags::Register_##type(&FLAG_##name, #name, default_value, comment);

#define DEFINE_FLAG_HANDLER(handler, name, comment)                            \
  bool DUMMY_##name = Flags::RegisterFlagHandler(handler, #name, comment);

#define DEFINE_OPTION_HANDLER(handler, name, comment)                          \
  bool DUMMY_##name = Flags::RegisterOptionHandler(handler, #name, comment);

namespace dart {

typedef void (*FlagHandler)(bool value);
typedef void (*OptionHandler)(const char* value);

// Forward declarations.
class Flag;
class JSONArray;
class JSONStream;

class Flags {
 public:
  static bool Register_bool(bool* addr,
                            const char* name,
                            bool default_value,
                            const char* comment);

  static int Register_int(int* addr,
                          const char* name,
                          int default_value,
                          const char* comment);

  static uint64_t Register_uint64_t(uint64_t* addr,
                                    const char* name,
                                    uint64_t default_value,
                                    const char* comment);

  static const char* Register_charp(charp* addr,
                                    const char* name,
                                    const char* default_value,
                                    const char* comment);

  static bool RegisterFlagHandler(FlagHandler handler,
                                  const char* name,
                                  const char* comment);

  static bool RegisterOptionHandler(OptionHandler handler,
                                    const char* name,
                                    const char* comment);

  static char* ProcessCommandLineFlags(int argc, const char** argv);

  static Flag* Lookup(const char* name);

  static bool IsSet(const char* name);

  static bool Initialized() { return initialized_; }

  static void Cleanup();

#ifndef PRODUCT
  static void PrintJSON(JSONStream* js);
#endif  // !PRODUCT

  static bool SetFlag(const char* name, const char* value, const char** error);

 private:
  static Flag** flags_;
  static intptr_t capacity_;
  static intptr_t num_flags_;

  static bool initialized_;

  static void AddFlag(Flag* flag);

  static bool SetFlagFromString(Flag* flag, const char* argument);

  static void Parse(const char* option);

  static int CompareFlagNames(const void* left, const void* right);

  static void PrintFlags();

#ifndef PRODUCT
  static void PrintFlagToJSONArray(JSONArray* jsarr, const Flag* flag);
#endif  // !PRODUCT

  // Testing needs direct access to private methods.
  friend void Dart_TestParseFlags();

  DISALLOW_ALLOCATION();
  DISALLOW_IMPLICIT_CONSTRUCTORS(Flags);
};

#define PRODUCT_FLAG_MACRO(name, type, default_value, comment)                 \
  extern type FLAG_##name;

#if defined(DEBUG)
#define DEBUG_FLAG_MACRO(name, type, default_value, comment)                   \
  extern type FLAG_##name;
#else  // defined(DEBUG)
#define DEBUG_FLAG_MACRO(name, type, default_value, comment)                   \
  const type FLAG_##name = default_value;
#endif  // defined(DEBUG)

#if defined(PRODUCT) && defined(DART_PRECOMPILED_RUNTIME)
#define RELEASE_FLAG_MACRO(name, product_value, type, default_value, comment)  \
  const type FLAG_##name = product_value;
#define PRECOMPILE_FLAG_MACRO(name, precompiled_value, product_value, type,    \
                              default_value, comment)                          \
  const type FLAG_##name = precompiled_value;

#elif defined(PRODUCT)  // !PRECOMPILED
#define RELEASE_FLAG_MACRO(name, product_value, type, default_value, comment)  \
  const type FLAG_##name = product_value;
#define PRECOMPILE_FLAG_MACRO(name, precompiled_value, product_value, type,    \
                              default_value, comment)                          \
  const type FLAG_##name = product_value;

#elif defined(DART_PRECOMPILED_RUNTIME)  // !PRODUCT
#define RELEASE_FLAG_MACRO(name, product_value, type, default_value, comment)  \
  extern type FLAG_##name;
#define PRECOMPILE_FLAG_MACRO(name, precompiled_value, product_value, type,    \
                              default_value, comment)                          \
  const type FLAG_##name = precompiled_value;

#else  // !PRODUCT && !PRECOMPILED
#define RELEASE_FLAG_MACRO(name, product_value, type, default_value, comment)  \
  extern type FLAG_##name;
#define PRECOMPILE_FLAG_MACRO(name, precompiled_value, product_value, type,    \
                              default_value, comment)                          \
  extern type FLAG_##name;

#endif

// Now declare all flags here.
FLAG_LIST(PRODUCT_FLAG_MACRO,
          RELEASE_FLAG_MACRO,
          PRECOMPILE_FLAG_MACRO,
          DEBUG_FLAG_MACRO)

#undef RELEASE_FLAG_MACRO
#undef DEBUG_FLAG_MACRO
#undef PRODUCT_FLAG_MACRO
#undef PRECOMPILE_FLAG_MACRO

#if defined(DART_PRECOMPILER)
DECLARE_FLAG(bool, target_address_sanitizer);
DECLARE_FLAG(bool, target_memory_sanitizer);
DECLARE_FLAG(bool, target_thread_sanitizer);
#else
#if defined(USING_ADDRESS_SANITIZER)
constexpr bool FLAG_target_address_sanitizer = true;
#else
constexpr bool FLAG_target_address_sanitizer = false;
#endif
#if defined(USING_MEMORY_SANITIZER)
constexpr bool FLAG_target_memory_sanitizer = true;
#else
constexpr bool FLAG_target_memory_sanitizer = false;
#endif
#if defined(USING_THREAD_SANITIZER)
constexpr bool FLAG_target_thread_sanitizer = true;
#else
constexpr bool FLAG_target_thread_sanitizer = false;
#endif
#endif

DECLARE_FLAG(bool, experimental_shared_data);

}  // namespace dart

#endif  // RUNTIME_VM_FLAGS_H_
