summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--android/bazel.go12
-rw-r--r--apex/builder.go2
-rw-r--r--cc/bp2build.go6
-rw-r--r--cc/config/arm64_device.go30
-rw-r--r--cc/config/arm_device.go3
-rw-r--r--cc/config/bp2build.go178
-rw-r--r--cc/config/bp2build_test.go140
-rw-r--r--cc/config/global.go6
-rw-r--r--cc/config/x86_64_device.go39
-rw-r--r--cc/config/x86_device.go22
-rw-r--r--cc/library.go4
-rw-r--r--cc/tidy.go2
-rw-r--r--cmd/extract_linker/main.go52
-rwxr-xr-xscripts/hiddenapi/verify_overlaps.py261
-rwxr-xr-xscripts/hiddenapi/verify_overlaps_test.py193
15 files changed, 821 insertions, 129 deletions
diff --git a/android/bazel.go b/android/bazel.go
index e3d017fe6..6942d57ca 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -137,6 +137,7 @@ var (
// build/bazel explicitly.
"build/bazel":/* recursive = */ false,
"build/bazel/examples/android_app":/* recursive = */ true,
+ "build/bazel/examples/java":/* recursive = */ true,
"build/bazel/bazel_skylib":/* recursive = */ true,
"build/bazel/rules":/* recursive = */ true,
"build/bazel/rules_cc":/* recursive = */ true,
@@ -149,6 +150,7 @@ var (
// e.g. ERROR: Analysis of target '@soong_injection//mixed_builds:buildroot' failed
"external/bazelbuild-rules_android":/* recursive = */ true,
+ "prebuilts/jdk":/* recursive = */ true,
"prebuilts/sdk":/* recursive = */ false,
"prebuilts/sdk/tools":/* recursive = */ false,
"prebuilts/r8":/* recursive = */ false,
@@ -166,9 +168,10 @@ var (
"system/logging/liblog": Bp2BuildDefaultTrueRecursively,
"system/timezone/apex": Bp2BuildDefaultTrueRecursively,
"system/timezone/output_data": Bp2BuildDefaultTrueRecursively,
- "external/jemalloc_new": Bp2BuildDefaultTrueRecursively,
- "external/fmtlib": Bp2BuildDefaultTrueRecursively,
"external/arm-optimized-routines": Bp2BuildDefaultTrueRecursively,
+ "external/fmtlib": Bp2BuildDefaultTrueRecursively,
+ "external/jemalloc_new": Bp2BuildDefaultTrueRecursively,
+ "external/libcxxabi": Bp2BuildDefaultTrueRecursively,
"external/scudo": Bp2BuildDefaultTrueRecursively,
"prebuilts/clang/host/linux-x86": Bp2BuildDefaultTrueRecursively,
}
@@ -231,7 +234,10 @@ var (
// Per-module denylist to opt modules out of mixed builds. Such modules will
// still be generated via bp2build.
- mixedBuildsDisabledList = []string{}
+ mixedBuildsDisabledList = []string{
+ "libc++abi", // http://b/195970501, cc_library_static, duplicate symbols because it propagates libc objects.
+ "libc++demangle", // http://b/195970501, cc_library_static, duplicate symbols because it propagates libc objects.
+ }
// Used for quicker lookups
bp2buildModuleDoNotConvert = map[string]bool{}
diff --git a/apex/builder.go b/apex/builder.go
index 148f42f09..5baa5c0cc 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -75,7 +75,7 @@ var (
// by default set to (uid/gid/mode) = (1000/1000/0644)
// TODO(b/113082813) make this configurable using config.fs syntax
generateFsConfig = pctx.StaticRule("generateFsConfig", blueprint.RuleParams{
- Command: `( echo '/ 1000 1000 0755' ` +
+ Command: `( set -e; echo '/ 1000 1000 0755' ` +
`&& for i in ${ro_paths}; do echo "/$$i 1000 1000 0644"; done ` +
`&& for i in ${exec_paths}; do echo "/$$i 0 2000 0755"; done ` +
`&& ( tr ' ' '\n' <${out}.apklist | for i in ${apk_paths}; do read apk; echo "/$$i 0 2000 0755"; zipinfo -1 $$apk | sed "s:\(.*\):/$$i/\1 1000 1000 0644:"; done ) ) > ${out}`,
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 7a57477e3..1706d7271 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -240,6 +240,8 @@ type compilerAttributes struct {
// C++ options and sources
cppFlags bazel.StringListAttribute
srcs bazel.LabelListAttribute
+
+ rtti bazel.BoolAttribute
}
// bp2BuildParseCompilerProps returns copts, srcs and hdrs and other attributes.
@@ -249,6 +251,7 @@ func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Modul
var asFlags bazel.StringListAttribute
var conlyFlags bazel.StringListAttribute
var cppFlags bazel.StringListAttribute
+ var rtti bazel.BoolAttribute
// Creates the -I flags for a directory, while making the directory relative
// to the exec root for Bazel to work.
@@ -302,6 +305,7 @@ func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Modul
asFlags.Value = parseCommandLineFlags(baseCompilerProps.Asflags)
conlyFlags.Value = parseCommandLineFlags(baseCompilerProps.Conlyflags)
cppFlags.Value = parseCommandLineFlags(baseCompilerProps.Cppflags)
+ rtti.Value = baseCompilerProps.Rtti
for _, dir := range parseLocalIncludeDirs(baseCompilerProps) {
copts.Value = append(copts.Value, includeFlags(dir)...)
@@ -345,6 +349,7 @@ func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Modul
asFlags.SetSelectValue(axis, config, archVariantAsflags)
conlyFlags.SetSelectValue(axis, config, parseCommandLineFlags(baseCompilerProps.Conlyflags))
cppFlags.SetSelectValue(axis, config, parseCommandLineFlags(baseCompilerProps.Cppflags))
+ rtti.SetSelectValue(axis, config, baseCompilerProps.Rtti)
}
}
}
@@ -380,6 +385,7 @@ func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Modul
cSrcs: cSrcs,
conlyFlags: conlyFlags,
cppFlags: cppFlags,
+ rtti: rtti,
}
}
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 812a245d2..2d6bcb89d 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -96,31 +96,25 @@ func init() {
pctx.SourcePathVariable("Arm64GccRoot",
"prebuilts/gcc/${HostPrebuiltTag}/aarch64/aarch64-linux-android-${arm64GccVersion}")
- pctx.StaticVariable("Arm64Ldflags", strings.Join(arm64Ldflags, " "))
- pctx.StaticVariable("Arm64Lldflags", strings.Join(arm64Lldflags, " "))
+ exportStringListStaticVariable("Arm64Ldflags", arm64Ldflags)
+ exportStringListStaticVariable("Arm64Lldflags", arm64Lldflags)
- pctx.StaticVariable("Arm64Cflags", strings.Join(arm64Cflags, " "))
- pctx.StaticVariable("Arm64Cppflags", strings.Join(arm64Cppflags, " "))
+ exportStringListStaticVariable("Arm64Cflags", arm64Cflags)
+ exportStringListStaticVariable("Arm64Cppflags", arm64Cppflags)
+
+ exportedStringListDictVars.Set("Arm64ArchVariantCflags", arm64ArchVariantCflags)
+ exportedStringListDictVars.Set("Arm64CpuVariantCflags", arm64CpuVariantCflags)
pctx.StaticVariable("Arm64Armv8ACflags", strings.Join(arm64ArchVariantCflags["armv8-a"], " "))
pctx.StaticVariable("Arm64Armv8ABranchProtCflags", strings.Join(arm64ArchVariantCflags["armv8-a-branchprot"], " "))
pctx.StaticVariable("Arm64Armv82ACflags", strings.Join(arm64ArchVariantCflags["armv8-2a"], " "))
pctx.StaticVariable("Arm64Armv82ADotprodCflags", strings.Join(arm64ArchVariantCflags["armv8-2a-dotprod"], " "))
- pctx.StaticVariable("Arm64CortexA53Cflags",
- strings.Join(arm64CpuVariantCflags["cortex-a53"], " "))
-
- pctx.StaticVariable("Arm64CortexA55Cflags",
- strings.Join(arm64CpuVariantCflags["cortex-a55"], " "))
-
- pctx.StaticVariable("Arm64KryoCflags",
- strings.Join(arm64CpuVariantCflags["kryo"], " "))
-
- pctx.StaticVariable("Arm64ExynosM1Cflags",
- strings.Join(arm64CpuVariantCflags["exynos-m1"], " "))
-
- pctx.StaticVariable("Arm64ExynosM2Cflags",
- strings.Join(arm64CpuVariantCflags["exynos-m2"], " "))
+ pctx.StaticVariable("Arm64CortexA53Cflags", strings.Join(arm64CpuVariantCflags["cortex-a53"], " "))
+ pctx.StaticVariable("Arm64CortexA55Cflags", strings.Join(arm64CpuVariantCflags["cortex-a55"], " "))
+ pctx.StaticVariable("Arm64KryoCflags", strings.Join(arm64CpuVariantCflags["kryo"], " "))
+ pctx.StaticVariable("Arm64ExynosM1Cflags", strings.Join(arm64CpuVariantCflags["exynos-m1"], " "))
+ pctx.StaticVariable("Arm64ExynosM2Cflags", strings.Join(arm64CpuVariantCflags["exynos-m2"], " "))
}
var (
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index b5afe408f..0fe5e6883 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -188,6 +188,9 @@ func init() {
exportStringListStaticVariable("ArmArmCflags", armArmCflags)
exportStringListStaticVariable("ArmThumbCflags", armThumbCflags)
+ exportedStringListDictVars.Set("ArmArchVariantCflags", armArchVariantCflags)
+ exportedStringListDictVars.Set("ArmCpuVariantCflags", armCpuVariantCflags)
+
// Clang arch variant cflags
exportStringListStaticVariable("ArmArmv7ACflags", armArchVariantCflags["armv7-a"])
exportStringListStaticVariable("ArmArmv7ANeonCflags", armArchVariantCflags["armv7-a-neon"])
diff --git a/cc/config/bp2build.go b/cc/config/bp2build.go
index e7e94a8a1..d19f5ac8e 100644
--- a/cc/config/bp2build.go
+++ b/cc/config/bp2build.go
@@ -15,98 +15,182 @@
package config
import (
- "android/soong/android"
"fmt"
"regexp"
+ "sort"
"strings"
)
+const (
+ bazelIndent = 4
+)
+
+type bazelVarExporter interface {
+ asBazel(exportedStringVariables, exportedStringListVariables) []bazelConstant
+}
+
// Helpers for exporting cc configuration information to Bazel.
var (
// Map containing toolchain variables that are independent of the
// environment variables of the build.
- exportedStringListVars = exportedStringListVariables{}
- exportedStringVars = exportedStringVariables{}
+ exportedStringListVars = exportedStringListVariables{}
+ exportedStringVars = exportedStringVariables{}
+ exportedStringListDictVars = exportedStringListDictVariables{}
)
+// Ensure that string s has no invalid characters to be generated into the bzl file.
+func validateCharacters(s string) string {
+ for _, c := range []string{`\n`, `"`, `\`} {
+ if strings.Contains(s, c) {
+ panic(fmt.Errorf("%s contains illegal character %s", s, c))
+ }
+ }
+ return s
+}
+
+type bazelConstant struct {
+ variableName string
+ internalDefinition string
+}
+
type exportedStringVariables map[string]string
-type exportedStringListVariables map[string][]string
func (m exportedStringVariables) Set(k string, v string) {
m[k] = v
}
+func bazelIndention(level int) string {
+ return strings.Repeat(" ", level*bazelIndent)
+}
+
+func printBazelList(items []string, indentLevel int) string {
+ list := make([]string, 0, len(items)+2)
+ list = append(list, "[")
+ innerIndent := bazelIndention(indentLevel + 1)
+ for _, item := range items {
+ list = append(list, fmt.Sprintf(`%s"%s",`, innerIndent, item))
+ }
+ list = append(list, bazelIndention(indentLevel)+"]")
+ return strings.Join(list, "\n")
+}
+
+func (m exportedStringVariables) asBazel(stringScope exportedStringVariables, stringListScope exportedStringListVariables) []bazelConstant {
+ ret := make([]bazelConstant, 0, len(m))
+ for k, variableValue := range m {
+ expandedVar := expandVar(variableValue, exportedStringVars, exportedStringListVars)
+ if len(expandedVar) > 1 {
+ panic(fmt.Errorf("%s expands to more than one string value: %s", variableValue, expandedVar))
+ }
+ ret = append(ret, bazelConstant{
+ variableName: k,
+ internalDefinition: fmt.Sprintf(`"%s"`, validateCharacters(expandedVar[0])),
+ })
+ }
+ return ret
+}
+
// Convenience function to declare a static variable and export it to Bazel's cc_toolchain.
func exportStringStaticVariable(name string, value string) {
pctx.StaticVariable(name, value)
exportedStringVars.Set(name, value)
}
+type exportedStringListVariables map[string][]string
+
func (m exportedStringListVariables) Set(k string, v []string) {
m[k] = v
}
+func (m exportedStringListVariables) asBazel(stringScope exportedStringVariables, stringListScope exportedStringListVariables) []bazelConstant {
+ ret := make([]bazelConstant, 0, len(m))
+ // For each exported variable, recursively expand elements in the variableValue
+ // list to ensure that interpolated variables are expanded according to their values
+ // in the variable scope.
+ for k, variableValue := range m {
+ var expandedVars []string
+ for _, v := range variableValue {
+ expandedVars = append(expandedVars, expandVar(v, stringScope, stringListScope)...)
+ }
+ // Assign the list as a bzl-private variable; this variable will be exported
+ // out through a constants struct later.
+ ret = append(ret, bazelConstant{
+ variableName: k,
+ internalDefinition: printBazelList(expandedVars, 0),
+ })
+ }
+ return ret
+}
+
// Convenience function to declare a static variable and export it to Bazel's cc_toolchain.
func exportStringListStaticVariable(name string, value []string) {
pctx.StaticVariable(name, strings.Join(value, " "))
exportedStringListVars.Set(name, value)
}
+type exportedStringListDictVariables map[string]map[string][]string
+
+func (m exportedStringListDictVariables) Set(k string, v map[string][]string) {
+ m[k] = v
+}
+
+func printBazelStringListDict(dict map[string][]string) string {
+ bazelDict := make([]string, 0, len(dict)+2)
+ bazelDict = append(bazelDict, "{")
+ for k, v := range dict {
+ bazelDict = append(bazelDict,
+ fmt.Sprintf(`%s"%s": %s,`, bazelIndention(1), k, printBazelList(v, 1)))
+ }
+ bazelDict = append(bazelDict, "}")
+ return strings.Join(bazelDict, "\n")
+}
+
+// Since dictionaries are not supported in Ninja, we do not expand variables for dictionaries
+func (m exportedStringListDictVariables) asBazel(_ exportedStringVariables, _ exportedStringListVariables) []bazelConstant {
+ ret := make([]bazelConstant, 0, len(m))
+ for k, dict := range m {
+ ret = append(ret, bazelConstant{
+ variableName: k,
+ internalDefinition: printBazelStringListDict(dict),
+ })
+ }
+ return ret
+}
+
// BazelCcToolchainVars generates bzl file content containing variables for
// Bazel's cc_toolchain configuration.
func BazelCcToolchainVars() string {
+ return bazelToolchainVars(
+ exportedStringListDictVars,
+ exportedStringListVars,
+ exportedStringVars)
+}
+
+func bazelToolchainVars(vars ...bazelVarExporter) string {
ret := "# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.\n\n"
- // Ensure that string s has no invalid characters to be generated into the bzl file.
- validateCharacters := func(s string) string {
- for _, c := range []string{`\n`, `"`, `\`} {
- if strings.Contains(s, c) {
- panic(fmt.Errorf("%s contains illegal character %s", s, c))
- }
- }
- return s
+ results := []bazelConstant{}
+ for _, v := range vars {
+ results = append(results, v.asBazel(exportedStringVars, exportedStringListVars)...)
}
- // For each exported variable, recursively expand elements in the variableValue
- // list to ensure that interpolated variables are expanded according to their values
- // in the variable scope.
- for _, k := range android.SortedStringKeys(exportedStringListVars) {
- variableValue := exportedStringListVars[k]
- var expandedVars []string
- for _, v := range variableValue {
- expandedVars = append(expandedVars, expandVar(v, exportedStringVars, exportedStringListVars)...)
- }
- // Build the list for this variable.
- list := "["
- for _, flag := range expandedVars {
- list += fmt.Sprintf("\n \"%s\",", validateCharacters(flag))
- }
- list += "\n]"
- // Assign the list as a bzl-private variable; this variable will be exported
- // out through a constants struct later.
- ret += fmt.Sprintf("_%s = %s\n", k, list)
- ret += "\n"
- }
+ sort.Slice(results, func(i, j int) bool { return results[i].variableName < results[j].variableName })
- for _, k := range android.SortedStringKeys(exportedStringVars) {
- variableValue := exportedStringVars[k]
- expandedVar := expandVar(variableValue, exportedStringVars, exportedStringListVars)
- if len(expandedVar) > 1 {
- panic(fmt.Errorf("%s expands to more than one string value: %s", variableValue, expandedVar))
- }
- ret += fmt.Sprintf("_%s = \"%s\"\n", k, validateCharacters(expandedVar[0]))
- ret += "\n"
+ definitions := make([]string, 0, len(results))
+ constants := make([]string, 0, len(results))
+ for _, b := range results {
+ definitions = append(definitions,
+ fmt.Sprintf("_%s = %s", b.variableName, b.internalDefinition))
+ constants = append(constants,
+ fmt.Sprintf("%[1]s%[2]s = _%[2]s,", bazelIndention(1), b.variableName))
}
// Build the exported constants struct.
+ ret += strings.Join(definitions, "\n\n")
+ ret += "\n\n"
ret += "constants = struct(\n"
- for _, k := range android.SortedStringKeys(exportedStringVars) {
- ret += fmt.Sprintf(" %s = _%s,\n", k, k)
- }
- for _, k := range android.SortedStringKeys(exportedStringListVars) {
- ret += fmt.Sprintf(" %s = _%s,\n", k, k)
- }
- ret += ")"
+ ret += strings.Join(constants, "\n")
+ ret += "\n)"
+
return ret
}
diff --git a/cc/config/bp2build_test.go b/cc/config/bp2build_test.go
index a4745e609..883597ad3 100644
--- a/cc/config/bp2build_test.go
+++ b/cc/config/bp2build_test.go
@@ -115,3 +115,143 @@ func TestExpandVars(t *testing.T) {
})
}
}
+
+func TestBazelToolchainVars(t *testing.T) {
+ testCases := []struct {
+ name string
+ vars []bazelVarExporter
+ expectedOut string
+ }{
+ {
+ name: "exports strings",
+ vars: []bazelVarExporter{
+ exportedStringVariables{
+ "a": "b",
+ "c": "d",
+ },
+ },
+ expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
+
+_a = "b"
+
+_c = "d"
+
+constants = struct(
+ a = _a,
+ c = _c,
+)`,
+ },
+ {
+ name: "exports string lists",
+ vars: []bazelVarExporter{
+ exportedStringListVariables{
+ "a": []string{"b1", "b2"},
+ "c": []string{"d1", "d2"},
+ },
+ },
+ expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
+
+_a = [
+ "b1",
+ "b2",
+]
+
+_c = [
+ "d1",
+ "d2",
+]
+
+constants = struct(
+ a = _a,
+ c = _c,
+)`,
+ },
+ {
+ name: "exports string lists dicts",
+ vars: []bazelVarExporter{
+ exportedStringListDictVariables{
+ "a": map[string][]string{"b1": []string{"b2"}},
+ "c": map[string][]string{"d1": []string{"d2"}},
+ },
+ },
+ expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
+
+_a = {
+ "b1": [
+ "b2",
+ ],
+}
+
+_c = {
+ "d1": [
+ "d2",
+ ],
+}
+
+constants = struct(
+ a = _a,
+ c = _c,
+)`,
+ },
+ {
+ name: "sorts across types",
+ vars: []bazelVarExporter{
+ exportedStringVariables{
+ "b": "b-val",
+ "d": "d-val",
+ },
+ exportedStringListVariables{
+ "c": []string{"c-val"},
+ "e": []string{"e-val"},
+ },
+ exportedStringListDictVariables{
+ "a": map[string][]string{"a1": []string{"a2"}},
+ "f": map[string][]string{"f1": []string{"f2"}},
+ },
+ },
+ expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
+
+_a = {
+ "a1": [
+ "a2",
+ ],
+}
+
+_b = "b-val"
+
+_c = [
+ "c-val",
+]
+
+_d = "d-val"
+
+_e = [
+ "e-val",
+]
+
+_f = {
+ "f1": [
+ "f2",
+ ],
+}
+
+constants = struct(
+ a = _a,
+ b = _b,
+ c = _c,
+ d = _d,
+ e = _e,
+ f = _f,
+)`,
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ out := bazelToolchainVars(tc.vars...)
+ if out != tc.expectedOut {
+ t.Errorf("Expected \n%s, got \n%s", tc.expectedOut, out)
+ }
+ })
+ }
+}
diff --git a/cc/config/global.go b/cc/config/global.go
index 3daee3845..248822f14 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -228,6 +228,8 @@ var (
"-Wno-non-c-typedef-for-linkage", // http://b/161304145
// New warnings to be fixed after clang-r407598
"-Wno-string-concatenation", // http://b/175068488
+ // New warnings to be fixed after clang-r428724
+ "-Wno-align-mismatch", // http://b/193679946
}
// Extra cflags for external third-party projects to disable warnings that
@@ -266,8 +268,8 @@ var (
// prebuilts/clang default settings.
ClangDefaultBase = "prebuilts/clang/host"
- ClangDefaultVersion = "clang-r416183b1"
- ClangDefaultShortVersion = "12.0.7"
+ ClangDefaultVersion = "clang-r428724"
+ ClangDefaultShortVersion = "13.0.1"
// Directories with warnings from Android.bp files.
WarningAllowedProjects = []string{
diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go
index c4f47a724..00f07ff26 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -77,6 +77,14 @@ var (
"popcnt": []string{"-mpopcnt"},
"aes_ni": []string{"-maes"},
}
+
+ x86_64DefaultArchVariantFeatures = []string{
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "popcnt",
+ }
)
const (
@@ -84,37 +92,38 @@ const (
)
func init() {
- android.RegisterDefaultArchVariantFeatures(android.Android, android.X86_64,
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "popcnt")
+ android.RegisterDefaultArchVariantFeatures(android.Android, android.X86_64, x86_64DefaultArchVariantFeatures...)
+ exportedStringListVars.Set("X86_64DefaultArchVariantFeatures", x86_64DefaultArchVariantFeatures)
pctx.StaticVariable("x86_64GccVersion", x86_64GccVersion)
pctx.SourcePathVariable("X86_64GccRoot",
"prebuilts/gcc/${HostPrebuiltTag}/x86/x86_64-linux-android-${x86_64GccVersion}")
- pctx.StaticVariable("X86_64ToolchainCflags", "-m64")
- pctx.StaticVariable("X86_64ToolchainLdflags", "-m64")
+ exportStringListStaticVariable("X86_64ToolchainCflags", []string{"-m64"})
+ exportStringListStaticVariable("X86_64ToolchainLdflags", []string{"-m64"})
- pctx.StaticVariable("X86_64Ldflags", strings.Join(x86_64Ldflags, " "))
- pctx.StaticVariable("X86_64Lldflags", strings.Join(x86_64Ldflags, " "))
+ exportStringListStaticVariable("X86_64Ldflags", x86_64Ldflags)
+ exportStringListStaticVariable("X86_64Lldflags", x86_64Ldflags)
// Clang cflags
- pctx.StaticVariable("X86_64Cflags", strings.Join(x86_64Cflags, " "))
- pctx.StaticVariable("X86_64Cppflags", strings.Join(x86_64Cppflags, " "))
+ exportStringListStaticVariable("X86_64Cflags", x86_64Cflags)
+ exportStringListStaticVariable("X86_64Cppflags", x86_64Cppflags)
// Yasm flags
- pctx.StaticVariable("X86_64YasmFlags", "-f elf64 -m amd64")
+ exportStringListStaticVariable("X86_64YasmFlags", []string{
+ "-f elf64",
+ "-m amd64",
+ })
// Extended cflags
+ exportedStringListDictVars.Set("X86_64ArchVariantCflags", x86_64ArchVariantCflags)
+ exportedStringListDictVars.Set("X86_64ArchFeatureCflags", x86_64ArchFeatureCflags)
+
// Architecture variant cflags
for variant, cflags := range x86_64ArchVariantCflags {
- pctx.StaticVariable("X86_64"+variant+"VariantCflags",
- strings.Join(cflags, " "))
+ pctx.StaticVariable("X86_64"+variant+"VariantCflags", strings.Join(cflags, " "))
}
}
diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go
index 5e510a494..29f059303 100644
--- a/cc/config/x86_device.go
+++ b/cc/config/x86_device.go
@@ -97,25 +97,29 @@ func init() {
pctx.SourcePathVariable("X86GccRoot",
"prebuilts/gcc/${HostPrebuiltTag}/x86/x86_64-linux-android-${x86GccVersion}")
- pctx.StaticVariable("X86ToolchainCflags", "-m32")
- pctx.StaticVariable("X86ToolchainLdflags", "-m32")
+ exportStringListStaticVariable("X86ToolchainCflags", []string{"-m32"})
+ exportStringListStaticVariable("X86ToolchainLdflags", []string{"-m32"})
- pctx.StaticVariable("X86Ldflags", strings.Join(x86Ldflags, " "))
- pctx.StaticVariable("X86Lldflags", strings.Join(x86Ldflags, " "))
+ exportStringListStaticVariable("X86Ldflags", x86Ldflags)
+ exportStringListStaticVariable("X86Lldflags", x86Ldflags)
// Clang cflags
- pctx.StaticVariable("X86Cflags", strings.Join(x86Cflags, " "))
- pctx.StaticVariable("X86Cppflags", strings.Join(x86Cppflags, " "))
+ exportStringListStaticVariable("X86Cflags", x86Cflags)
+ exportStringListStaticVariable("X86Cppflags", x86Cppflags)
// Yasm flags
- pctx.StaticVariable("X86YasmFlags", "-f elf32 -m x86")
+ exportStringListStaticVariable("X86YasmFlags", []string{
+ "-f elf32",
+ "-m x86",
+ })
// Extended cflags
+ exportedStringListDictVars.Set("X86ArchVariantCflags", x86ArchVariantCflags)
+ exportedStringListDictVars.Set("X86ArchFeatureCflags", x86ArchFeatureCflags)
// Architecture variant cflags
for variant, cflags := range x86ArchVariantCflags {
- pctx.StaticVariable("X86"+variant+"VariantCflags",
- strings.Join(cflags, " "))
+ pctx.StaticVariable("X86"+variant+"VariantCflags", strings.Join(cflags, " "))
}
}
diff --git a/cc/library.go b/cc/library.go
index 1478a1623..51cba208e 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -236,6 +236,7 @@ type bazelCcLibraryAttributes struct {
Includes bazel.StringListAttribute
Linkopts bazel.StringListAttribute
Use_libcrt bazel.BoolAttribute
+ Rtti bazel.BoolAttribute
// This is shared only.
Version_script bazel.LabelAttribute
@@ -323,6 +324,7 @@ func CcLibraryBp2Build(ctx android.TopDownMutatorContext) {
Includes: exportedIncludes,
Linkopts: linkerAttrs.linkopts,
Use_libcrt: linkerAttrs.useLibcrt,
+ Rtti: compilerAttrs.rtti,
Version_script: linkerAttrs.versionScript,
@@ -2335,6 +2337,7 @@ type bazelCcLibraryStaticAttributes struct {
Linkopts bazel.StringListAttribute
Linkstatic bool
Use_libcrt bazel.BoolAttribute
+ Rtti bazel.BoolAttribute
Includes bazel.StringListAttribute
Hdrs bazel.LabelListAttribute
@@ -2396,6 +2399,7 @@ func ccLibraryStaticBp2BuildInternal(ctx android.TopDownMutatorContext, module *
Linkopts: linkerAttrs.linkopts,
Linkstatic: true,
Use_libcrt: linkerAttrs.useLibcrt,
+ Rtti: compilerAttrs.rtti,
Includes: exportedIncludes,
Cppflags: compilerAttrs.cppFlags,
diff --git a/cc/tidy.go b/cc/tidy.go
index b2382e88a..fefa7f0f1 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -146,6 +146,8 @@ func (tidy *tidyFeature) flags(ctx ModuleContext, flags Flags) Flags {
tidyChecks = tidyChecks + ",-bugprone-signed-char-misuse"
// http://b/155034972
tidyChecks = tidyChecks + ",-bugprone-branch-clone"
+ // http://b/193716442
+ tidyChecks = tidyChecks + ",-bugprone-implicit-widening-of-multiplication-result"
flags.TidyFlags = append(flags.TidyFlags, tidyChecks)
if ctx.Config().IsEnvTrue("WITH_TIDY") {
diff --git a/cmd/extract_linker/main.go b/cmd/extract_linker/main.go
index f1f7bc74b..1280553a5 100644
--- a/cmd/extract_linker/main.go
+++ b/cmd/extract_linker/main.go
@@ -26,7 +26,7 @@ import (
"io/ioutil"
"log"
"os"
- "strings"
+ "strconv"
)
func main() {
@@ -59,20 +59,16 @@ func main() {
fmt.Fprintln(script, "ENTRY(__dlwrap__start)")
fmt.Fprintln(script, "SECTIONS {")
+ progsWithFlagsCount := make(map[string]int)
+
for _, prog := range ef.Progs {
if prog.Type != elf.PT_LOAD {
continue
}
- var progName string
- progSection := progToFirstSection(prog, ef.Sections)
- if progSection != nil {
- progName = progSection.Name
- } else {
- progName = fmt.Sprintf(".sect%d", load)
- }
- sectionName := ".linker" + progName
- symName := "__dlwrap_linker" + strings.ReplaceAll(progName, ".", "_")
+ progName := progNameFromFlags(prog.Flags, progsWithFlagsCount)
+ sectionName := ".linker_" + progName
+ symName := "__dlwrap_linker_" + progName
flags := ""
if prog.Flags&elf.PF_W != 0 {
@@ -83,6 +79,12 @@ func main() {
}
fmt.Fprintf(asm, ".section %s, \"a%s\"\n", sectionName, flags)
+ if load == 0 {
+ fmt.Fprintln(asm, ".globl __dlwrap_linker")
+ fmt.Fprintln(asm, "__dlwrap_linker:")
+ fmt.Fprintln(asm)
+ }
+
fmt.Fprintf(asm, ".globl %s\n%s:\n\n", symName, symName)
fmt.Fprintf(script, " %s 0x%x : {\n", sectionName, baseLoadAddr+prog.Vaddr)
@@ -106,6 +108,10 @@ func main() {
load += 1
}
+ fmt.Fprintln(asm, ".globl __dlwrap_linker_end")
+ fmt.Fprintln(asm, "__dlwrap_linker_end:")
+ fmt.Fprintln(asm)
+
fmt.Fprintln(asm, `.section .note.android.embedded_linker,"a",%note`)
fmt.Fprintln(script, "}")
@@ -139,11 +145,25 @@ func bytesToAsm(asm io.Writer, buf []byte) {
fmt.Fprintln(asm)
}
-func progToFirstSection(prog *elf.Prog, sections []*elf.Section) *elf.Section {
- for _, section := range sections {
- if section.Addr == prog.Vaddr {
- return section
- }
+func progNameFromFlags(flags elf.ProgFlag, progsWithFlagsCount map[string]int) string {
+ s := ""
+ if flags&elf.PF_R != 0 {
+ s += "r"
+ }
+ if flags&elf.PF_W != 0 {
+ s += "w"
}
- return nil
+ if flags&elf.PF_X != 0 {
+ s += "x"
+ }
+
+ count := progsWithFlagsCount[s]
+ count++
+ progsWithFlagsCount[s] = count
+
+ if count > 1 {
+ s += strconv.Itoa(count)
+ }
+
+ return s
}
diff --git a/scripts/hiddenapi/verify_overlaps.py b/scripts/hiddenapi/verify_overlaps.py
index 8579321aa..a4a423ed6 100755
--- a/scripts/hiddenapi/verify_overlaps.py
+++ b/scripts/hiddenapi/verify_overlaps.py
@@ -19,12 +19,244 @@ Verify that one set of hidden API flags is a subset of another.
import argparse
import csv
+import sys
from itertools import chain
+class InteriorNode:
+ """
+ An interior node in a trie.
+
+ Each interior node has a dict that maps from an element of a signature to
+ either another interior node or a leaf. Each interior node represents either
+ a package, class or nested class. Class members are represented by a Leaf.
+
+ Associating the set of flags [public-api] with the signature
+ "Ljava/lang/Object;->String()Ljava/lang/String;" will cause the following
+ nodes to be created:
+ Node()
+ ^- package:java -> Node()
+ ^- package:lang -> Node()
+ ^- class:Object -> Node()
+ ^- member:String()Ljava/lang/String; -> Leaf([public-api])
+
+ Associating the set of flags [blocked,core-platform-api] with the signature
+ "Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;"
+ will cause the following nodes to be created:
+ Node()
+ ^- package:java -> Node()
+ ^- package:lang -> Node()
+ ^- class:Character -> Node()
+ ^- class:UnicodeScript -> Node()
+ ^- member:of(I)Ljava/lang/Character$UnicodeScript;
+ -> Leaf([blocked,core-platform-api])
+
+ Attributes:
+ nodes: a dict from an element of the signature to the Node/Leaf
+ containing the next element/value.
+ """
+ def __init__(self):
+ self.nodes = {}
+
+ def signatureToElements(self, signature):
+ """
+ Split a signature or a prefix into a number of elements:
+ 1. The packages (excluding the leading L preceding the first package).
+ 2. The class names, from outermost to innermost.
+ 3. The member signature.
+
+ e.g. Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;
+ will be broken down into these elements:
+ 1. package:java
+ 2. package:lang
+ 3. class:Character
+ 4. class:UnicodeScript
+ 5. member:of(I)Ljava/lang/Character$UnicodeScript;
+ """
+ # Remove the leading L.
+ # - java/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;
+ text = signature.removeprefix("L")
+ # Split the signature between qualified class name and the class member
+ # signature.
+ # 0 - java/lang/Character$UnicodeScript
+ # 1 - of(I)Ljava/lang/Character$UnicodeScript;
+ parts = text.split(";->")
+ member = parts[1:]
+ # Split the qualified class name into packages, and class name.
+ # 0 - java
+ # 1 - lang
+ # 2 - Character$UnicodeScript
+ elements = parts[0].split("/")
+ packages = elements[0:-1]
+ className = elements[-1]
+ if className == "*" or className == "**":
+ # Cannot specify a wildcard and target a specific member
+ if len(member) != 0:
+ raise Exception("Invalid signature %s: contains wildcard %s and member signature %s"
+ % (signature, className, member[0]))
+ wildcard = [className]
+ # Assemble the parts into a single list, adding prefixes to identify
+ # the different parts.
+ # 0 - package:java
+ # 1 - package:lang
+ # 2 - *
+ return list(chain(map(lambda x : "package:" + x, packages),
+ wildcard))
+ else:
+ # Split the class name into outer / inner classes
+ # 0 - Character
+ # 1 - UnicodeScript
+ classes = className.split("$")
+ # Assemble the parts into a single list, adding prefixes to identify
+ # the different parts.
+ # 0 - package:java
+ # 1 - package:lang
+ # 2 - class:Character
+ # 3 - class:UnicodeScript
+ # 4 - member:of(I)Ljava/lang/Character$UnicodeScript;
+ return list(chain(map(lambda x : "package:" + x, packages),
+ map(lambda x : "class:" + x, classes),
+ map(lambda x : "member:" + x, member)))
+
+ def add(self, signature, value):
+ """
+ Associate the value with the specific signature.
+ :param signature: the member signature
+ :param value: the value to associated with the signature
+ :return: n/a
+ """
+ # Split the signature into elements.
+ elements = self.signatureToElements(signature)
+ # Find the Node associated with the deepest class.
+ node = self
+ for element in elements[:-1]:
+ if element in node.nodes:
+ node = node.nodes[element]
+ else:
+ next = InteriorNode()
+ node.nodes[element] = next
+ node = next
+ # Add a Leaf containing the value and associate it with the member
+ # signature within the class.
+ lastElement = elements[-1]
+ if not lastElement.startswith("member:"):
+ raise Exception("Invalid signature: %s, does not identify a specific member" % signature)
+ if lastElement in node.nodes:
+ raise Exception("Duplicate signature: %s" % signature)
+ node.nodes[lastElement] = Leaf(value)
+
+ def getMatchingRows(self, pattern):
+ """
+ Get the values (plural) associated with the pattern.
+
+ e.g. If the pattern is a full signature then this will return a list
+ containing the value associated with that signature.
+
+ If the pattern is a class then this will return a list containing the
+ values associated with all members of that class.
+
+ If the pattern is a package then this will return a list containing the
+ values associated with all the members of all the classes in that
+ package and sub-packages.
+
+ If the pattern ends with "*" then the preceding part is treated as a
+ package and this will return a list containing the values associated
+ with all the members of all the classes in that package.
+
+ If the pattern ends with "**" then the preceding part is treated
+ as a package and this will return a list containing the values
+ associated with all the members of all the classes in that package and
+ all sub-packages.
+
+ :param pattern: the pattern which could be a complete signature or a
+ class, or package wildcard.
+ :return: an iterable containing all the values associated with the
+ pattern.
+ """
+ elements = self.signatureToElements(pattern)
+ node = self
+ # Include all values from this node and all its children.
+ selector = lambda x : True
+ lastElement = elements[-1]
+ if lastElement == "*" or lastElement == "**":
+ elements = elements[:-1]
+ if lastElement == "*":
+ # Do not include values from sub-packages.
+ selector = lambda x : not x.startswith("package:")
+ for element in elements:
+ if element in node.nodes:
+ node = node.nodes[element]
+ else:
+ return []
+ return chain.from_iterable(node.values(selector))
+
+ def values(self, selector):
+ """
+ :param selector: a function that can be applied to a key in the nodes
+ attribute to determine whether to return its values.
+ :return: A list of iterables of all the values associated with this
+ node and its children.
+ """
+ values = []
+ self.appendValues(values, selector)
+ return values
+
+ def appendValues(self, values, selector):
+ """
+ Append the values associated with this node and its children to the
+ list.
+
+ For each item (key, child) in nodes the child node's values are returned
+ if and only if the selector returns True when called on its key. A child
+ node's values are all the values associated with it and all its
+ descendant nodes.
+
+ :param selector: a function that can be applied to a key in the nodes
+ attribute to determine whether to return its values.
+ :param values: a list of a iterables of values.
+ """
+ for key, node in self.nodes.items():
+ if selector(key):
+ node.appendValues(values, lambda x : True)
+
+class Leaf:
+ """
+ A leaf of the trie
+
+ Attributes:
+ value: the value associated with this leaf.
+ """
+ def __init__(self, value):
+ self.value = value
+
+ def values(self, selector):
+ """
+ :return: A list of a list of the value associated with this node.
+ """
+ return [[self.value]]
+
+ def appendValues(self, values, selector):
+ """
+ Appends a list of the value associated with this node to the list.
+ :param values: a list of a iterables of values.
+ """
+ values.append([self.value])
+
def dict_reader(input):
return csv.DictReader(input, delimiter=',', quotechar='|', fieldnames=['signature'])
-def extract_subset_from_monolithic_flags_as_dict_from_file(monolithicFlagsDict, patternsFile):
+def read_flag_trie_from_file(file):
+ with open(file, 'r') as stream:
+ return read_flag_trie_from_stream(stream)
+
+def read_flag_trie_from_stream(stream):
+ trie = InteriorNode()
+ reader = dict_reader(stream)
+ for row in reader:
+ signature = row['signature']
+ trie.add(signature, row)
+ return trie
+
+def extract_subset_from_monolithic_flags_as_dict_from_file(monolithicTrie, patternsFile):
"""
Extract a subset of flags from the dict containing all the monolithic flags.
@@ -34,21 +266,24 @@ def extract_subset_from_monolithic_flags_as_dict_from_file(monolithicFlagsDict,
:return: the dict from signature to row.
"""
with open(patternsFile, 'r') as stream:
- return extract_subset_from_monolithic_flags_as_dict_from_stream(monolithicFlagsDict, stream)
+ return extract_subset_from_monolithic_flags_as_dict_from_stream(monolithicTrie, stream)
-def extract_subset_from_monolithic_flags_as_dict_from_stream(monolithicFlagsDict, stream):
+def extract_subset_from_monolithic_flags_as_dict_from_stream(monolithicTrie, stream):
"""
- Extract a subset of flags from the dict containing all the monolithic flags.
+ Extract a subset of flags from the trie containing all the monolithic flags.
- :param monolithicFlagsDict: the dict containing all the monolithic flags.
+ :param monolithicTrie: the trie containing all the monolithic flags.
:param stream: a stream containing a list of signature patterns that define
the subset.
:return: the dict from signature to row.
"""
dict = {}
- for signature in stream:
- signature = signature.rstrip()
- dict[signature] = monolithicFlagsDict.get(signature, {})
+ for pattern in stream:
+ pattern = pattern.rstrip()
+ rows = monolithicTrie.getMatchingRows(pattern)
+ for row in rows:
+ signature = row['signature']
+ dict[signature] = row
return dict
def read_signature_csv_from_stream_as_dict(stream):
@@ -108,20 +343,20 @@ def main(argv):
args_parser.add_argument('modularFlags', nargs=argparse.REMAINDER, help='Flags produced by individual bootclasspath_fragment modules')
args = args_parser.parse_args(argv[1:])
- # Read in the monolithic flags into a dict indexed by signature
+ # Read in all the flags into the trie
monolithicFlagsPath = args.monolithicFlags
- monolithicFlagsDict = read_signature_csv_from_file_as_dict(monolithicFlagsPath)
+ monolithicTrie = read_flag_trie_from_file(monolithicFlagsPath)
# For each subset specified on the command line, create dicts for the flags
- # provided by the subset and the corresponding flags from the complete set of
- # flags and compare them.
+ # provided by the subset and the corresponding flags from the complete set
+ # of flags and compare them.
failed = False
for modularPair in args.modularFlags:
parts = modularPair.split(":")
modularFlagsPath = parts[0]
modularPatternsPath = parts[1]
modularFlagsDict = read_signature_csv_from_file_as_dict(modularFlagsPath)
- monolithicFlagsSubsetDict = extract_subset_from_monolithic_flags_as_dict_from_file(monolithicFlagsDict, modularPatternsPath)
+ monolithicFlagsSubsetDict = extract_subset_from_monolithic_flags_as_dict_from_file(monolithicTrie, modularPatternsPath)
mismatchingSignatures = compare_signature_flags(monolithicFlagsSubsetDict, modularFlagsDict)
if mismatchingSignatures:
failed = True
diff --git a/scripts/hiddenapi/verify_overlaps_test.py b/scripts/hiddenapi/verify_overlaps_test.py
index b6d5fa38e..747725458 100755
--- a/scripts/hiddenapi/verify_overlaps_test.py
+++ b/scripts/hiddenapi/verify_overlaps_test.py
@@ -20,8 +20,52 @@ import unittest
from verify_overlaps import *
+class TestSignatureToElements(unittest.TestCase):
+
+ def signatureToElements(self, signature):
+ return InteriorNode().signatureToElements(signature)
+
+ def test_signatureToElements_1(self):
+ expected = [
+ 'package:java',
+ 'package:lang',
+ 'class:ProcessBuilder',
+ 'class:Redirect',
+ 'class:1',
+ 'member:<init>()V',
+ ]
+ self.assertEqual(expected, self.signatureToElements(
+ "Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V"))
+
+ def test_signatureToElements_2(self):
+ expected = [
+ 'package:java',
+ 'package:lang',
+ 'class:Object',
+ 'member:hashCode()I',
+ ]
+ self.assertEqual(expected, self.signatureToElements(
+ "Ljava/lang/Object;->hashCode()I"))
+
+ def test_signatureToElements_3(self):
+ expected = [
+ 'package:java',
+ 'package:lang',
+ 'class:CharSequence',
+ 'class:',
+ 'class:ExternalSyntheticLambda0',
+ 'member:<init>(Ljava/lang/CharSequence;)V',
+ ]
+ self.assertEqual(expected, self.signatureToElements(
+ "Ljava/lang/CharSequence$$ExternalSyntheticLambda0;"
+ "-><init>(Ljava/lang/CharSequence;)V"))
+
class TestDetectOverlaps(unittest.TestCase):
+ def read_flag_trie_from_string(self, csv):
+ with io.StringIO(csv) as f:
+ return read_flag_trie_from_stream(f)
+
def read_signature_csv_from_string_as_dict(self, csv):
with io.StringIO(csv) as f:
return read_signature_csv_from_stream_as_dict(f)
@@ -33,13 +77,14 @@ class TestDetectOverlaps(unittest.TestCase):
extractInput = '''
Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
Ljava/lang/Object;->toString()Ljava/lang/String;,blocked
+Ljava/util/zip/ZipFile;-><clinit>()V,blocked
+Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;,blocked
+Ljava/lang/Character;->serialVersionUID:J,sdk
+Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V,blocked
'''
- def test_extract_subset(self):
- monolithic = self.read_signature_csv_from_string_as_dict(TestDetectOverlaps.extractInput)
- modular = self.read_signature_csv_from_string_as_dict('''
-Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
-''')
+ def test_extract_subset_signature(self):
+ monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
patterns = 'Ljava/lang/Object;->hashCode()I'
@@ -52,6 +97,144 @@ Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
}
self.assertEqual(expected, subset)
+ def test_extract_subset_class(self):
+ monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+
+ patterns = 'java/lang/Object'
+
+ subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
+ expected = {
+ 'Ljava/lang/Object;->hashCode()I': {
+ None: ['public-api', 'system-api', 'test-api'],
+ 'signature': 'Ljava/lang/Object;->hashCode()I',
+ },
+ 'Ljava/lang/Object;->toString()Ljava/lang/String;': {
+ None: ['blocked'],
+ 'signature': 'Ljava/lang/Object;->toString()Ljava/lang/String;',
+ },
+ }
+ self.assertEqual(expected, subset)
+
+ def test_extract_subset_outer_class(self):
+ monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+
+ patterns = 'java/lang/Character'
+
+ subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
+ expected = {
+ 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;': {
+ None: ['blocked'],
+ 'signature': 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
+ },
+ 'Ljava/lang/Character;->serialVersionUID:J': {
+ None: ['sdk'],
+ 'signature': 'Ljava/lang/Character;->serialVersionUID:J',
+ },
+ }
+ self.assertEqual(expected, subset)
+
+ def test_extract_subset_nested_class(self):
+ monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+
+ patterns = 'java/lang/Character$UnicodeScript'
+
+ subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
+ expected = {
+ 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;': {
+ None: ['blocked'],
+ 'signature': 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
+ },
+ }
+ self.assertEqual(expected, subset)
+
+ def test_extract_subset_package(self):
+ monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+
+ patterns = 'java/lang/*'
+
+ subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
+ expected = {
+ 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;': {
+ None: ['blocked'],
+ 'signature': 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
+ },
+ 'Ljava/lang/Character;->serialVersionUID:J': {
+ None: ['sdk'],
+ 'signature': 'Ljava/lang/Character;->serialVersionUID:J',
+ },
+ 'Ljava/lang/Object;->hashCode()I': {
+ None: ['public-api', 'system-api', 'test-api'],
+ 'signature': 'Ljava/lang/Object;->hashCode()I',
+ },
+ 'Ljava/lang/Object;->toString()Ljava/lang/String;': {
+ None: ['blocked'],
+ 'signature': 'Ljava/lang/Object;->toString()Ljava/lang/String;',
+ },
+ 'Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V': {
+ None: ['blocked'],
+ 'signature': 'Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V',
+ },
+ }
+ self.assertEqual(expected, subset)
+
+ def test_extract_subset_recursive_package(self):
+ monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+
+ patterns = 'java/**'
+
+ subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
+ expected = {
+ 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;': {
+ None: ['blocked'],
+ 'signature': 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
+ },
+ 'Ljava/lang/Character;->serialVersionUID:J': {
+ None: ['sdk'],
+ 'signature': 'Ljava/lang/Character;->serialVersionUID:J',
+ },
+ 'Ljava/lang/Object;->hashCode()I': {
+ None: ['public-api', 'system-api', 'test-api'],
+ 'signature': 'Ljava/lang/Object;->hashCode()I',
+ },
+ 'Ljava/lang/Object;->toString()Ljava/lang/String;': {
+ None: ['blocked'],
+ 'signature': 'Ljava/lang/Object;->toString()Ljava/lang/String;',
+ },
+ 'Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V': {
+ None: ['blocked'],
+ 'signature': 'Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V',
+ },
+ 'Ljava/util/zip/ZipFile;-><clinit>()V': {
+ None: ['blocked'],
+ 'signature': 'Ljava/util/zip/ZipFile;-><clinit>()V',
+ },
+ }
+ self.assertEqual(expected, subset)
+
+ def test_extract_subset_invalid_pattern_wildcard_and_member(self):
+ monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+
+ patterns = 'Ljava/lang/*;->hashCode()I'
+
+ with self.assertRaises(Exception) as context:
+ self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
+ self.assertTrue("contains wildcard * and member signature hashCode()I" in str(context.exception))
+
+ def test_read_trie_duplicate(self):
+ with self.assertRaises(Exception) as context:
+ self.read_flag_trie_from_string('''
+Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
+Ljava/lang/Object;->hashCode()I,blocked
+''')
+ self.assertTrue("Duplicate signature: Ljava/lang/Object;->hashCode()I" in str(context.exception))
+
+ def test_read_trie_missing_member(self):
+ with self.assertRaises(Exception) as context:
+ self.read_flag_trie_from_string('''
+Ljava/lang/Object,public-api,system-api,test-api
+''')
+ self.assertTrue("Invalid signature: Ljava/lang/Object, does not identify a specific member" in str(context.exception))
+
def test_match(self):
monolithic = self.read_signature_csv_from_string_as_dict('''
Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api