// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package coverage

// Building the runtime package with coverage instrumentation enabled
// is tricky.  For all other packages, you can be guaranteed that
// the package init function is run before any functions are executed,
// but this invariant is not maintained for packages such as "runtime",
// "internal/cpu", etc. To handle this, hard-code the package ID for
// the set of packages whose functions may be running before the
// init function of the package is complete.
//
// Hardcoding is unfortunate because it means that the tool that does
// coverage instrumentation has to keep a list of runtime packages,
// meaning that if someone makes changes to the pkg "runtime"
// dependencies, unexpected behavior will result for coverage builds.
// The coverage runtime will detect and report the unexpected
// behavior; look for an error of this form:
//
//    internal error in coverage meta-data tracking:
//    list of hard-coded runtime package IDs needs revising.
//    registered list:
//    slot: 0 path='internal/cpu'  hard-coded id: 1
//    slot: 1 path='internal/goarch'  hard-coded id: 2
//    slot: 2 path='runtime/internal/atomic'  hard-coded id: 3
//    slot: 3 path='internal/goos'
//    slot: 4 path='runtime/internal/sys'  hard-coded id: 5
//    slot: 5 path='internal/abi'  hard-coded id: 4
//    slot: 6 path='runtime/internal/math'  hard-coded id: 6
//    slot: 7 path='internal/bytealg'  hard-coded id: 7
//    slot: 8 path='internal/goexperiment'
//    slot: 9 path='runtime/internal/syscall'  hard-coded id: 8
//    slot: 10 path='runtime'  hard-coded id: 9
//    fatal error: runtime.addCovMeta
//
// For the error above, the hard-coded list is missing "internal/goos"
// and "internal/goexperiment" ; the developer in question will need
// to copy the list above into "rtPkgs" below.
//
// Note: this strategy assumes that the list of dependencies of
// package runtime is fixed, and doesn't vary depending on OS/arch. If
// this were to be the case, we would need a table of some sort below
// as opposed to a fixed list.

var rtPkgs = [...]string{
	"internal/cpu",
	"internal/goarch",
	"runtime/internal/atomic",
	"internal/goos",
	"runtime/internal/sys",
	"internal/abi",
	"runtime/internal/math",
	"internal/bytealg",
	"internal/goexperiment",
	"runtime/internal/syscall",
	"runtime",
}

// Scoping note: the constants and apis in this file are internal
// only, not expected to ever be exposed outside of the runtime (unlike
// other coverage file formats and APIs, which will likely be shared
// at some point).

// NotHardCoded is a package pseudo-ID indicating that a given package
// is not part of the runtime and doesn't require a hard-coded ID.
const NotHardCoded = -1

// HardCodedPkgID returns the hard-coded ID for the specified package
// path, or -1 if we don't use a hard-coded ID. Hard-coded IDs start
// at -2 and decrease as we go down the list.
func HardCodedPkgID(pkgpath string) int {
	for k, p := range rtPkgs {
		if p == pkgpath {
			return (0 - k) - 2
		}
	}
	return NotHardCoded
}
