Make signature_to_elements stricter and more consistent
Previously, signature_to_elements would return a string array where
non-wildcard strings were of the form <type>:<value> but wildcard
strings were just * or **. This change makes it handle wildcards
consistently with the other element types and adds some extra
checking for edge cases.
Bug: 202154151
Test: m out/soong/hiddenapi/hiddenapi-flags.csv
atest --host signature_trie_test verify_overlaps_test
pyformat -s 4 --force_quote_type double -i scripts/hiddenapi/signature_trie*
/usr/bin/pylint --rcfile $ANDROID_BUILD_TOP/tools/repohooks/tools/pylintrc scripts/hiddenapi/signature_trie*
Change-Id: I5bfaf5e75c7da54b6241f68e03231939c9d65501
diff --git a/scripts/hiddenapi/signature_trie.py b/scripts/hiddenapi/signature_trie.py
index 2ea8c49..2b0973a 100644
--- a/scripts/hiddenapi/signature_trie.py
+++ b/scripts/hiddenapi/signature_trie.py
@@ -110,46 +110,69 @@
# 0 - java/lang/Character$UnicodeScript
# 1 - of(I)Ljava/lang/Character$UnicodeScript;
parts = text.split(";->")
+ # If there is no member then this will be an empty list.
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]
- class_name = elements[-1]
- if class_name in ("*", "**"): # pylint: disable=no-else-return
+ last_element = elements[-1]
+ wildcard = []
+ classes = []
+ if "*" in last_element:
+ if last_element not in ("*", "**"):
+ raise Exception(f"Invalid signature '{signature}': invalid "
+ f"wildcard '{last_element}'")
+ packages = elements[0:-1]
# Cannot specify a wildcard and target a specific member
- if len(member) != 0:
- raise Exception(f"Invalid signature {signature}: contains "
- f"wildcard {class_name} and "
- f"member signature {member[0]}")
- wildcard = [class_name]
- # Assemble the parts into a single list, adding prefixes to identify
- # the different parts.
- # 0 - package:java
- # 1 - package:lang
- # 2 - *
- return list(chain(["package:" + x for x in packages], wildcard))
+ if member:
+ raise Exception(f"Invalid signature '{signature}': contains "
+ f"wildcard '{last_element}' and "
+ f"member signature '{member[0]}'")
+ wildcard = [last_element]
+ elif last_element.islower():
+ raise Exception(f"Invalid signature '{signature}': last element "
+ f"'{last_element}' is lower case but should be an "
+ f"upper case class name or wildcard")
else:
+ packages = elements[0:-1]
# Split the class name into outer / inner classes
# 0 - Character
# 1 - UnicodeScript
- classes = class_name.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(["package:" + x for x in packages],
- ["class:" + x for x in classes],
- ["member:" + x for x in member]))
+ classes = last_element.removesuffix(";").split("$")
+
+ # Assemble the parts into a single list, adding prefixes to identify
+ # the different parts. If a wildcard is provided then it looks something
+ # like this:
+ # 0 - package:java
+ # 1 - package:lang
+ # 2 - *
+ #
+ # Otherwise, it looks something like this:
+ # 0 - package:java
+ # 1 - package:lang
+ # 2 - class:Character
+ # 3 - class:UnicodeScript
+ # 4 - member:of(I)Ljava/lang/Character$UnicodeScript;
+ return list(
+ chain([f"package:{x}" for x in packages],
+ [f"class:{x}" for x in classes],
+ [f"member:{x}" for x in member],
+ [f"wildcard:{x}" for x in wildcard]))
# pylint: enable=line-too-long
+ @staticmethod
+ def split_element(element):
+ element_type, element_value = element.split(":", 1)
+ return element_type, element_value
+
+ @staticmethod
+ def element_type(element):
+ element_type, _ = InteriorNode.split_element(element)
+ return element_type
+
def add(self, signature, value):
"""Associate the value with the specific signature.
@@ -171,7 +194,8 @@
# Add a Leaf containing the value and associate it with the member
# signature within the class.
last_element = elements[-1]
- if not last_element.startswith("member:"):
+ last_element_type = self.element_type(last_element)
+ if last_element_type != "member":
raise Exception(
f"Invalid signature: {signature}, does not identify a "
"specific member")
@@ -213,11 +237,12 @@
selector = lambda x: True
last_element = elements[-1]
- if last_element in ("*", "**"):
+ last_element_type, last_element_value = self.split_element(last_element)
+ if last_element_type == "wildcard":
elements = elements[:-1]
- if last_element == "*":
+ if last_element_value == "*":
# Do not include values from sub-packages.
- selector = lambda x: not x.startswith("package:")
+ selector = lambda x: InteriorNode.element_type(x) != "package"
for element in elements:
if element in node.nodes:
@@ -237,7 +262,6 @@
node.append_values(values, lambda x: True)
-
@dataclasses.dataclass()
class Leaf(Node):
"""A leaf of the trie"""