# Copyright 2022 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import("//base/allocator/partition_allocator/partition_alloc.gni")
import("//build/buildflag_header.gni")
import("//build/config/chromecast_build.gni")
import("//build/config/chromeos/ui_mode.gni")
import("//build/config/dcheck_always_on.gni")
import("//build/config/logging.gni")

# Add partition_alloc.gni and import it for partition_alloc configs.

config("partition_alloc_implementation") {
  # See also: `partition_alloc_base/component_export.h`
  defines = [ "IS_PARTITION_ALLOC_IMPL" ]
}

config("memory_tagging") {
  if (current_cpu == "arm64" && is_clang &&
      (is_linux || is_chromeos || is_android || is_fuchsia)) {
    # base/ has access to the MTE intrinsics because it needs to use them,
    # but they're not backwards compatible. Use base::CPU::has_mte()
    # beforehand to confirm or use indirect functions (ifuncs) to select
    # an MTE-specific implementation at dynamic link-time.
    cflags = [
      "-Xclang",
      "-target-feature",
      "-Xclang",
      "+mte",
    ]
  }
}

if (is_fuchsia) {
  config("fuchsia_sync_lib") {
    libs = [
      "sync",  # Used by spinning_mutex.h.
    ]
  }
}

if (enable_pkeys && is_debug) {
  config("no_stack_protector") {
    cflags = [ "-fno-stack-protector" ]
  }
}

component("partition_alloc") {
  sources = [
    "address_pool_manager.cc",
    "address_pool_manager.h",
    "address_pool_manager_bitmap.cc",
    "address_pool_manager_bitmap.h",
    "address_pool_manager_types.h",
    "address_space_randomization.cc",
    "address_space_randomization.h",
    "address_space_stats.h",
    "allocation_guard.cc",
    "allocation_guard.h",
    "compressed_pointer.cc",
    "compressed_pointer.h",
    "dangling_raw_ptr_checks.cc",
    "dangling_raw_ptr_checks.h",
    "freeslot_bitmap.h",
    "freeslot_bitmap_constants.h",
    "gwp_asan_support.cc",
    "gwp_asan_support.h",
    "memory_reclaimer.cc",
    "memory_reclaimer.h",
    "oom.cc",
    "oom.h",
    "oom_callback.cc",
    "oom_callback.h",
    "page_allocator.cc",
    "page_allocator.h",
    "page_allocator_constants.h",
    "page_allocator_internal.h",
    "partition_address_space.cc",
    "partition_address_space.h",
    "partition_alloc-inl.h",
    "partition_alloc.cc",
    "partition_alloc.h",
    "partition_alloc_base/atomic_ref_count.h",
    "partition_alloc_base/augmentations/compiler_specific.h",
    "partition_alloc_base/bit_cast.h",
    "partition_alloc_base/bits.h",
    "partition_alloc_base/check.cc",
    "partition_alloc_base/check.h",
    "partition_alloc_base/compiler_specific.h",
    "partition_alloc_base/component_export.h",
    "partition_alloc_base/cpu.cc",
    "partition_alloc_base/cpu.h",
    "partition_alloc_base/cxx17_backports.h",
    "partition_alloc_base/cxx20_is_constant_evaluated.h",
    "partition_alloc_base/debug/alias.cc",
    "partition_alloc_base/debug/alias.h",
    "partition_alloc_base/gtest_prod_util.h",
    "partition_alloc_base/immediate_crash.h",
    "partition_alloc_base/logging.cc",
    "partition_alloc_base/logging.h",
    "partition_alloc_base/memory/ref_counted.cc",
    "partition_alloc_base/memory/ref_counted.h",
    "partition_alloc_base/memory/scoped_policy.h",
    "partition_alloc_base/memory/scoped_refptr.h",
    "partition_alloc_base/no_destructor.h",
    "partition_alloc_base/numerics/checked_math.h",
    "partition_alloc_base/numerics/checked_math_impl.h",
    "partition_alloc_base/numerics/clamped_math.h",
    "partition_alloc_base/numerics/clamped_math_impl.h",
    "partition_alloc_base/numerics/math_constants.h",
    "partition_alloc_base/numerics/ostream_operators.h",
    "partition_alloc_base/numerics/ranges.h",
    "partition_alloc_base/numerics/safe_conversions.h",
    "partition_alloc_base/numerics/safe_conversions_arm_impl.h",
    "partition_alloc_base/numerics/safe_conversions_impl.h",
    "partition_alloc_base/numerics/safe_math.h",
    "partition_alloc_base/numerics/safe_math_arm_impl.h",
    "partition_alloc_base/numerics/safe_math_clang_gcc_impl.h",
    "partition_alloc_base/numerics/safe_math_shared_impl.h",
    "partition_alloc_base/posix/eintr_wrapper.h",
    "partition_alloc_base/rand_util.cc",
    "partition_alloc_base/rand_util.h",
    "partition_alloc_base/scoped_clear_last_error.h",
    "partition_alloc_base/strings/stringprintf.cc",
    "partition_alloc_base/strings/stringprintf.h",
    "partition_alloc_base/system/sys_info.h",
    "partition_alloc_base/thread_annotations.h",
    "partition_alloc_base/threading/platform_thread.cc",
    "partition_alloc_base/threading/platform_thread.h",
    "partition_alloc_base/threading/platform_thread_ref.h",
    "partition_alloc_base/time/time.cc",
    "partition_alloc_base/time/time.h",
    "partition_alloc_base/time/time_override.cc",
    "partition_alloc_base/time/time_override.h",
    "partition_alloc_base/types/strong_alias.h",
    "partition_alloc_base/win/win_handle_types.h",
    "partition_alloc_base/win/win_handle_types_list.inc",
    "partition_alloc_base/win/windows_types.h",
    "partition_alloc_check.h",
    "partition_alloc_config.h",
    "partition_alloc_constants.h",
    "partition_alloc_forward.h",
    "partition_alloc_hooks.cc",
    "partition_alloc_hooks.h",
    "partition_alloc_notreached.h",
    "partition_bucket.cc",
    "partition_bucket.h",
    "partition_bucket_lookup.h",
    "partition_cookie.h",
    "partition_direct_map_extent.h",
    "partition_freelist_entry.h",
    "partition_lock.h",
    "partition_oom.cc",
    "partition_oom.h",
    "partition_page.cc",
    "partition_page.h",
    "partition_ref_count.h",
    "partition_root.cc",
    "partition_root.h",
    "partition_stats.cc",
    "partition_stats.h",
    "partition_tls.h",
    "pkey.cc",
    "pkey.h",
    "random.cc",
    "random.h",
    "reservation_offset_table.cc",
    "reservation_offset_table.h",
    "reverse_bytes.h",
    "spinning_mutex.cc",
    "spinning_mutex.h",
    "tagging.cc",
    "tagging.h",
    "internal_allocator.cc",
    "internal_allocator.h",
    "thread_cache.cc",
    "thread_cache.h",
    "yield_processor.h",
  ]

  if (use_starscan) {
    sources += [
      "starscan/logging.h",
      "starscan/metadata_allocator.cc",
      "starscan/metadata_allocator.h",
      "starscan/pcscan.cc",
      "starscan/pcscan.h",
      "starscan/pcscan_internal.cc",
      "starscan/pcscan_internal.h",
      "starscan/pcscan_scheduling.cc",
      "starscan/pcscan_scheduling.h",
      "starscan/raceful_worklist.h",
      "starscan/scan_loop.h",
      "starscan/snapshot.cc",
      "starscan/snapshot.h",
      "starscan/stack/stack.cc",
      "starscan/stack/stack.h",
      "starscan/starscan_fwd.h",
      "starscan/state_bitmap.h",
      "starscan/stats_collector.cc",
      "starscan/stats_collector.h",
      "starscan/stats_reporter.h",
      "starscan/write_protector.cc",
      "starscan/write_protector.h",
    ]
  }

  defines = []
  if (is_win) {
    sources += [
      "page_allocator_internals_win.h",
      "partition_alloc_base/rand_util_win.cc",
      "partition_alloc_base/scoped_clear_last_error_win.cc",
      "partition_alloc_base/threading/platform_thread_win.cc",
      "partition_alloc_base/time/time_win.cc",
      "partition_tls_win.cc",
    ]
  } else if (is_posix) {
    sources += [
      "page_allocator_internals_posix.cc",
      "page_allocator_internals_posix.h",
      "partition_alloc_base/files/file_util.h",
      "partition_alloc_base/files/file_util_posix.cc",
      "partition_alloc_base/posix/safe_strerror.cc",
      "partition_alloc_base/posix/safe_strerror.h",
      "partition_alloc_base/rand_util_posix.cc",
      "partition_alloc_base/threading/platform_thread_internal_posix.h",
      "partition_alloc_base/threading/platform_thread_posix.cc",
      "partition_alloc_base/time/time_conversion_posix.cc",
    ]

    if (is_android || is_chromeos_ash) {
      sources += [ "partition_alloc_base/time/time_android.cc" ]
    }
    if (is_apple) {
      sources += [ "partition_alloc_base/time/time_mac.mm" ]
    } else {
      sources += [ "partition_alloc_base/time/time_now_posix.cc" ]
    }
  } else if (is_fuchsia) {
    sources += [
      "page_allocator_internals_fuchsia.h",
      "partition_alloc_base/fuchsia/fuchsia_logging.cc",
      "partition_alloc_base/fuchsia/fuchsia_logging.h",
      "partition_alloc_base/posix/safe_strerror.cc",
      "partition_alloc_base/posix/safe_strerror.h",
      "partition_alloc_base/rand_util_fuchsia.cc",
      "partition_alloc_base/threading/platform_thread_internal_posix.h",
      "partition_alloc_base/threading/platform_thread_posix.cc",
      "partition_alloc_base/time/time_conversion_posix.cc",
      "partition_alloc_base/time/time_fuchsia.cc",
    ]
  }
  if (is_android) {
    # Only android build requires native_library, and native_library depends
    # on file_path. So file_path is added if is_android = true.
    sources += [
      "partition_alloc_base/files/file_path.cc",
      "partition_alloc_base/files/file_path.h",
      "partition_alloc_base/native_library.cc",
      "partition_alloc_base/native_library.h",
      "partition_alloc_base/native_library_posix.cc",
    ]
  }
  if (is_apple) {
    # Apple-specific utilities
    sources += [
      "partition_alloc_base/mac/foundation_util.h",
      "partition_alloc_base/mac/foundation_util.mm",
      "partition_alloc_base/mac/scoped_cftyperef.h",
      "partition_alloc_base/mac/scoped_typeref.h",
    ]
    if (is_ios) {
      sources += [
        "partition_alloc_base/ios/ios_util.h",
        "partition_alloc_base/ios/ios_util.mm",
        "partition_alloc_base/system/sys_info_ios.mm",
      ]
    }
    if (is_mac) {
      sources += [
        "partition_alloc_base/mac/mac_util.h",
        "partition_alloc_base/mac/mac_util.mm",
        "partition_alloc_base/system/sys_info_mac.mm",
      ]
    }
  }
  if (use_starscan) {
    if (current_cpu == "x64") {
      assert(pcscan_stack_supported)
      sources += [ "starscan/stack/asm/x64/push_registers_asm.cc" ]
    } else if (current_cpu == "x86") {
      assert(pcscan_stack_supported)
      sources += [ "starscan/stack/asm/x86/push_registers_asm.cc" ]
    } else if (current_cpu == "arm") {
      assert(pcscan_stack_supported)
      sources += [ "starscan/stack/asm/arm/push_registers_asm.cc" ]
    } else if (current_cpu == "arm64") {
      assert(pcscan_stack_supported)
      sources += [ "starscan/stack/asm/arm64/push_registers_asm.cc" ]
    } else {
      # To support a trampoline for another arch, please refer to v8/src/heap/base.
      assert(!pcscan_stack_supported)
    }
  }
  public_deps = [
    ":chromecast_buildflags",
    ":chromeos_buildflags",
    ":debugging_buildflags",
    ":logging_buildflags",
    ":partition_alloc_buildflags",
  ]

  configs += [
    ":partition_alloc_implementation",
    ":memory_tagging",
  ]
  deps = []
  public_configs = []
  if (is_android) {
    # tagging.cc requires __arm_mte_set_* functions.
    deps += [ "//third_party/android_ndk:cpu_features" ]
  }
  if (is_fuchsia) {
    public_deps += [
      "//third_party/fuchsia-sdk/sdk/pkg/fit",
      "//third_party/fuchsia-sdk/sdk/pkg/sync",
      "//third_party/fuchsia-sdk/sdk/pkg/zx",
    ]

    # Needed for users of spinning_mutex.h, which for performance reasons,
    # contains inlined calls to `libsync` inside the header file.
    # It appends an entry to the "libs" section of the dependent target.
    public_configs += [ ":fuchsia_sync_lib" ]
  }

  frameworks = []
  if (is_mac) {
    # SecTaskGetCodeSignStatus needs:
    frameworks += [ "Security.framework" ]
  }

  if (is_apple) {
    frameworks += [
      "CoreFoundation.framework",
      "Foundation.framework",
    ]
  }

  configs += [ "//build/config/compiler:wexit_time_destructors" ]

  # Partition alloc is relatively hot (>1% of cycles for users of CrOS). Use speed-focused
  # optimizations for it.
  if (!is_debug) {
    configs -= [ "//build/config/compiler:default_optimization" ]
    configs += [ "//build/config/compiler:optimize_speed" ]
  }

  # We want to be able to test pkey mode without access to the default pkey.
  # This is incompatible with stack protectors since the TLS won't be pkey-tagged.
  if (enable_pkeys && is_debug) {
    configs += [ ":no_stack_protector" ]
  }
}

source_set("raw_ptr") {
  # `gn check` is unhappy with most `#includes` when PA isn't
  # actually built.
  check_includes = use_partition_alloc
  public = [
    "pointers/raw_ptr.h",
    "pointers/raw_ptr_exclusion.h",
    "pointers/raw_ref.h",
  ]
  sources = []
  if (enable_backup_ref_ptr_support) {
    sources += [
      "pointers/raw_ptr_backup_ref_impl.cc",
      "pointers/raw_ptr_backup_ref_impl.h",
    ]
  } else if (use_hookable_raw_ptr) {
    sources += [
      "pointers/raw_ptr_hookable_impl.cc",
      "pointers/raw_ptr_hookable_impl.h",
    ]
  } else if (use_asan_unowned_ptr) {
    sources += [
      "pointers/raw_ptr_asan_unowned_impl.cc",
      "pointers/raw_ptr_asan_unowned_impl.h",
    ]
  }
  if (use_partition_alloc) {
    public_deps = [ ":partition_alloc" ]
  }
  deps = [ ":buildflags" ]

  # See also: `partition_alloc_base/component_export.h`
  defines = [ "IS_RAW_PTR_IMPL" ]

  # When built inside Chromium, although this cannot directly be made a
  # component, we expect `//base` to provide the only GN-level access.
  if (build_with_chromium) {
    visibility = [ "//base" ]
  }
}

buildflag_header("partition_alloc_buildflags") {
  header = "partition_alloc_buildflags.h"

  _record_alloc_info = false

  # GWP-ASan is tied to BRP's "refcount in previous slot" mode, whose
  # enablement is already gated on BRP enablement.
  _enable_gwp_asan_support = put_ref_count_in_previous_slot

  # TODO(crbug.com/1151236): Need to refactor the following buildflags.
  # The buildflags (except RECORD_ALLOC_INFO) are used by both chrome and
  # partition alloc. For partition alloc,
  # gen/base/allocator/partition_allocator/partition_alloc_buildflags.h
  # defines and partition alloc includes the header file. For chrome,
  # gen/base/allocator/buildflags.h defines and chrome includes.
  flags = [
    "HAS_64_BIT_POINTERS=$has_64_bit_pointers",

    "USE_PARTITION_ALLOC=$use_partition_alloc",
    "USE_PARTITION_ALLOC_AS_MALLOC=$use_partition_alloc_as_malloc",

    "ENABLE_BACKUP_REF_PTR_SUPPORT=$enable_backup_ref_ptr_support",
    "ENABLE_BACKUP_REF_PTR_SLOW_CHECKS=$enable_backup_ref_ptr_slow_checks",
    "ENABLE_BACKUP_REF_PTR_FEATURE_FLAG=$enable_backup_ref_ptr_feature_flag",
    "ENABLE_RAW_PTR_EXPERIMENTAL=$enable_raw_ptr_experimental",
    "ENABLE_DANGLING_RAW_PTR_CHECKS=$enable_dangling_raw_ptr_checks",
    "ENABLE_DANGLING_RAW_PTR_FEATURE_FLAG=$enable_dangling_raw_ptr_feature_flag",
    "ENABLE_DANGLING_RAW_PTR_PERF_EXPERIMENT=$enable_dangling_raw_ptr_perf_experiment",
    "ENABLE_POINTER_SUBTRACTION_CHECK=$enable_pointer_subtraction_check",
    "BACKUP_REF_PTR_POISON_OOB_PTR=$backup_ref_ptr_poison_oob_ptr",
    "PUT_REF_COUNT_IN_PREVIOUS_SLOT=$put_ref_count_in_previous_slot",
    "USE_ASAN_BACKUP_REF_PTR=$use_asan_backup_ref_ptr",
    "USE_ASAN_UNOWNED_PTR=$use_asan_unowned_ptr",
    "USE_HOOKABLE_RAW_PTR=$use_hookable_raw_ptr",
    "ENABLE_GWP_ASAN_SUPPORT=$_enable_gwp_asan_support",

    "FORCE_ENABLE_RAW_PTR_EXCLUSION=$force_enable_raw_ptr_exclusion",

    "RECORD_ALLOC_INFO=$_record_alloc_info",
    "USE_FREESLOT_BITMAP=$use_freeslot_bitmap",
    "GLUE_CORE_POOLS=$glue_core_pools",
    "ENABLE_POINTER_COMPRESSION=$enable_pointer_compression_support",
    "ENABLE_SHADOW_METADATA_FOR_64_BITS_POINTERS=$enable_shadow_metadata",

    "USE_STARSCAN=$use_starscan",
    "PCSCAN_STACK_SUPPORTED=$pcscan_stack_supported",

    "ENABLE_PKEYS=$enable_pkeys",
  ]

  if (is_apple) {
    # TODO(crbug.com/1414153): once TimeTicks::Now behavior is unified on iOS,
    # this should be removed.
    flags += [ "PARTITION_ALLOC_ENABLE_MACH_ABSOLUTE_TIME_TICKS=" +
               "$partition_alloc_enable_mach_absolute_time_ticks" ]
  }
}

buildflag_header("chromecast_buildflags") {
  header = "chromecast_buildflags.h"

  flags = [
    "PA_IS_CAST_ANDROID=$is_cast_android",
    "PA_IS_CASTOS=$is_castos",
  ]
}

buildflag_header("chromeos_buildflags") {
  header = "chromeos_buildflags.h"

  flags = [ "PA_IS_CHROMEOS_ASH=$is_chromeos_ash" ]
}

buildflag_header("logging_buildflags") {
  header = "logging_buildflags.h"

  flags = [ "PA_ENABLE_LOG_ERROR_NOT_REACHED=$enable_log_error_not_reached" ]
}

buildflag_header("debugging_buildflags") {
  header = "debugging_buildflags.h"
  header_dir = rebase_path(".", "//") + "/partition_alloc_base/debug"

  # Duplicates the setup Chromium uses to define `DCHECK_IS_ON()`,
  # but avails it as a buildflag.
  _dcheck_is_on = is_debug || dcheck_always_on

  flags = [
    "PA_DCHECK_IS_ON=$_dcheck_is_on",
    "PA_EXPENSIVE_DCHECKS_ARE_ON=$enable_expensive_dchecks",
    "PA_DCHECK_IS_CONFIGURABLE=$dcheck_is_configurable",
  ]
}

group("buildflags") {
  public_deps = [
    ":chromecast_buildflags",
    ":chromeos_buildflags",
    ":debugging_buildflags",
    ":logging_buildflags",
    ":partition_alloc_buildflags",
  ]
}
# TODO(crbug.com/1151236): After making partition_alloc a standalone library,
# move test code here. i.e. test("partition_alloc_tests") { ... } and
# test("partition_alloc_perftests").