Loading scripts/hiddenapi/signature_trie.py +55 −31 Original line number Diff line number Diff line Loading @@ -110,46 +110,69 @@ class InteriorNode(Node): # 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("/") 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] class_name = elements[-1] if class_name in ("*", "**"): # pylint: disable=no-else-return # 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("$") classes = last_element.removesuffix(";").split("$") # Assemble the parts into a single list, adding prefixes to identify # the different parts. # 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(["package:" + x for x in packages], ["class:" + x for x in classes], ["member:" + x for x in member])) 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. Loading @@ -171,7 +194,8 @@ class InteriorNode(Node): # 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") Loading Loading @@ -213,11 +237,12 @@ class InteriorNode(Node): 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: Loading @@ -237,7 +262,6 @@ class InteriorNode(Node): node.append_values(values, lambda x: True) @dataclasses.dataclass() class Leaf(Node): """A leaf of the trie""" Loading scripts/hiddenapi/signature_trie_test.py +21 −6 Original line number Diff line number Diff line Loading @@ -77,7 +77,7 @@ class TestSignatureToElements(unittest.TestCase): elements = [ "package:java", "package:lang", "*", "wildcard:*", ] signature = "java/lang/*" self.assertEqual(elements, self.signature_to_elements(signature)) Loading @@ -86,25 +86,33 @@ class TestSignatureToElements(unittest.TestCase): elements = [ "package:java", "package:lang", "**", "wildcard:**", ] signature = "java/lang/**" self.assertEqual(elements, self.signature_to_elements(signature)) def test_no_packages_wildcard(self): elements = [ "*", "wildcard:*", ] signature = "*" self.assertEqual(elements, self.signature_to_elements(signature)) def test_no_packages_recursive_wildcard(self): elements = [ "**", "wildcard:**", ] signature = "**" self.assertEqual(elements, self.signature_to_elements(signature)) def test_invalid_no_class_or_wildcard(self): signature = "java/lang" with self.assertRaises(Exception) as context: self.signature_to_elements(signature) self.assertIn( "last element 'lang' is lower case but should be an " "upper case class name or wildcard", str(context.exception)) def test_non_standard_class_name(self): elements = [ "package:javax", Loading @@ -114,11 +122,18 @@ class TestSignatureToElements(unittest.TestCase): signature = "Ljavax/crypto/extObjectInputStream" self.assertEqual(elements, self.signature_to_elements(signature)) def test_invalid_pattern_wildcard(self): pattern = "Ljava/lang/Class*" with self.assertRaises(Exception) as context: self.signature_to_elements(pattern) self.assertIn("invalid wildcard 'Class*'", str(context.exception)) def test_invalid_pattern_wildcard_and_member(self): pattern = "Ljava/lang/*;->hashCode()I" with self.assertRaises(Exception) as context: self.signature_to_elements(pattern) self.assertIn("contains wildcard * and member signature hashCode()I", self.assertIn( "contains wildcard '*' and member signature 'hashCode()I'", str(context.exception)) Loading Loading
scripts/hiddenapi/signature_trie.py +55 −31 Original line number Diff line number Diff line Loading @@ -110,46 +110,69 @@ class InteriorNode(Node): # 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("/") 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] class_name = elements[-1] if class_name in ("*", "**"): # pylint: disable=no-else-return # 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("$") classes = last_element.removesuffix(";").split("$") # Assemble the parts into a single list, adding prefixes to identify # the different parts. # 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(["package:" + x for x in packages], ["class:" + x for x in classes], ["member:" + x for x in member])) 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. Loading @@ -171,7 +194,8 @@ class InteriorNode(Node): # 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") Loading Loading @@ -213,11 +237,12 @@ class InteriorNode(Node): 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: Loading @@ -237,7 +262,6 @@ class InteriorNode(Node): node.append_values(values, lambda x: True) @dataclasses.dataclass() class Leaf(Node): """A leaf of the trie""" Loading
scripts/hiddenapi/signature_trie_test.py +21 −6 Original line number Diff line number Diff line Loading @@ -77,7 +77,7 @@ class TestSignatureToElements(unittest.TestCase): elements = [ "package:java", "package:lang", "*", "wildcard:*", ] signature = "java/lang/*" self.assertEqual(elements, self.signature_to_elements(signature)) Loading @@ -86,25 +86,33 @@ class TestSignatureToElements(unittest.TestCase): elements = [ "package:java", "package:lang", "**", "wildcard:**", ] signature = "java/lang/**" self.assertEqual(elements, self.signature_to_elements(signature)) def test_no_packages_wildcard(self): elements = [ "*", "wildcard:*", ] signature = "*" self.assertEqual(elements, self.signature_to_elements(signature)) def test_no_packages_recursive_wildcard(self): elements = [ "**", "wildcard:**", ] signature = "**" self.assertEqual(elements, self.signature_to_elements(signature)) def test_invalid_no_class_or_wildcard(self): signature = "java/lang" with self.assertRaises(Exception) as context: self.signature_to_elements(signature) self.assertIn( "last element 'lang' is lower case but should be an " "upper case class name or wildcard", str(context.exception)) def test_non_standard_class_name(self): elements = [ "package:javax", Loading @@ -114,11 +122,18 @@ class TestSignatureToElements(unittest.TestCase): signature = "Ljavax/crypto/extObjectInputStream" self.assertEqual(elements, self.signature_to_elements(signature)) def test_invalid_pattern_wildcard(self): pattern = "Ljava/lang/Class*" with self.assertRaises(Exception) as context: self.signature_to_elements(pattern) self.assertIn("invalid wildcard 'Class*'", str(context.exception)) def test_invalid_pattern_wildcard_and_member(self): pattern = "Ljava/lang/*;->hashCode()I" with self.assertRaises(Exception) as context: self.signature_to_elements(pattern) self.assertIn("contains wildcard * and member signature hashCode()I", self.assertIn( "contains wildcard '*' and member signature 'hashCode()I'", str(context.exception)) Loading