diff options
| -rw-r--r-- | android/bazel.go | 12 | ||||
| -rw-r--r-- | apex/builder.go | 2 | ||||
| -rw-r--r-- | cc/bp2build.go | 6 | ||||
| -rw-r--r-- | cc/config/arm64_device.go | 30 | ||||
| -rw-r--r-- | cc/config/arm_device.go | 3 | ||||
| -rw-r--r-- | cc/config/bp2build.go | 178 | ||||
| -rw-r--r-- | cc/config/bp2build_test.go | 140 | ||||
| -rw-r--r-- | cc/config/global.go | 6 | ||||
| -rw-r--r-- | cc/config/x86_64_device.go | 39 | ||||
| -rw-r--r-- | cc/config/x86_device.go | 22 | ||||
| -rw-r--r-- | cc/library.go | 4 | ||||
| -rw-r--r-- | cc/tidy.go | 2 | ||||
| -rw-r--r-- | cmd/extract_linker/main.go | 52 | ||||
| -rwxr-xr-x | scripts/hiddenapi/verify_overlaps.py | 261 | ||||
| -rwxr-xr-x | scripts/hiddenapi/verify_overlaps_test.py | 193 |
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 |