Add a tool for transforming method names to descriptors
Our various tools are not very consistent about using method
descriptor or names. Make a tool similar to c++filt that can transform
between them.
Currently this tool can only function when the line contains nothing
but the symbol so it's somewhat less useful then c++filt but it still
has some uses
Example:
```
% echo 'La/b/c;->foobar(IJ[[Ljava/lang/Long;)V' | ./tools/method-to-descriptor.py -r
void a.b.c.foobar(int,long,java.lang.Long[][])
% echo 'void my.awesome.parrot.says(java.lang.String,int[][][])' | ./tools/method-to-descriptor.py
Lmy/awesome/parrot;->says(Ljava/lang/String;[[[I)V
```
Test: manual
Change-Id: I2ea99cf7ad2c2934352780842933b8109f7da9f1
diff --git a/tools/method-to-descriptor.py b/tools/method-to-descriptor.py
new file mode 100755
index 0000000..b0bcf22
--- /dev/null
+++ b/tools/method-to-descriptor.py
@@ -0,0 +1,146 @@
+#!/usr/bin/python3
+#
+# Copyright 2020, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Converts a method to a descriptor or vice-versa.
+
+eg:
+
+% echo 'void myclass.foobar(long, java.lang.Object)' | method-to-descriptor.py
+Lmyclass;->foobar(jLjaga/lang/Object;)V
+% echo 'Lmyclass;->foobar(j)V' | method2descriptor.py -r
+void myclass.foobar(long)
+"""
+
+import argparse
+import sys
+
+
+def GetStdinLineIter():
+ """reads from stdin"""
+ return map(str.strip, sys.stdin)
+
+
+def readDescriptor(s):
+ """Reads a single descriptor and returns the string starting at the point after the descriptor"""
+ if s[0] == "[":
+ inner, rest = readDescriptor(s[1:])
+ return "[" + inner, rest
+ elif s[0] == "L":
+ type_end = s.index(";")
+ return s[:type_end + 1], s[type_end + 1:]
+ else:
+ assert s[0] in {"B", "C", "D", "F", "I", "J", "S", "Z", "V"}, s[0]
+ return s[0], s[1:]
+
+
+# Descriptor to name for basic types
+TYPE_MAP = {
+ "V": "void",
+ "B": "byte",
+ "C": "char",
+ "D": "double",
+ "F": "float",
+ "I": "int",
+ "J": "long",
+ "S": "short",
+ "Z": "boolean"
+}
+
+# Name to descriptor
+DESC_MAP = dict((y, x) for x, y in TYPE_MAP.items())
+
+def TypeDescriptorToName(desc):
+ """Turn a single type descirptor into a name"""
+ if desc[0] == "[":
+ inner = TypeDescriptorToName(desc[1:])
+ return inner + "[]"
+ elif desc[0] == "L":
+ assert desc[-1] == ";", desc
+ return desc[1:-1].replace("/", ".")
+ else:
+ return TYPE_MAP[desc]
+
+def DescriptorToName(desc):
+ """Turn a method descriptor into a name"""
+ class_name, rest = readDescriptor(desc)
+ assert rest[0:2] == "->", desc
+ rest = rest[2:]
+ args_start = rest.index("(")
+ func_name = rest[:args_start]
+ rest = rest[args_start + 1:]
+ args = []
+ while rest[0] != ")":
+ cur_arg, rest = readDescriptor(rest)
+ args.append(cur_arg)
+ rest = rest[1:]
+ return_type, rest = readDescriptor(rest)
+ assert rest.strip() == "", desc
+ return "{} {}.{}({})".format(
+ TypeDescriptorToName(return_type), TypeDescriptorToName(class_name),
+ func_name, ",".join(map(TypeDescriptorToName, args)))
+
+def SingleNameToDescriptor(name):
+ if name in DESC_MAP:
+ return DESC_MAP[name]
+ elif name.endswith("[]"):
+ return "[" + SingleNameToDescriptor(name[:-2])
+ elif name == "":
+ return ""
+ else:
+ return "L" + name.replace(".", "/") + ";"
+
+
+def NameToDescriptor(desc):
+ return_name = desc.split()[0]
+ name_and_args = desc.split()[1]
+ args_start = name_and_args.index("(")
+ names = name_and_args[0:args_start]
+ meth_split = names.rfind(".")
+ class_name = names[:meth_split]
+ meth_name = names[meth_split + 1:]
+ args = map(str.strip, name_and_args[args_start + 1:-1].split(","))
+ return "{}->{}({}){}".format(
+ SingleNameToDescriptor(class_name), meth_name,
+ "".join(map(SingleNameToDescriptor, args)),
+ SingleNameToDescriptor(return_name))
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ "method-to-descriptor.py",
+ description="Convert a java method-name/stream into it's descriptor or vice-versa."
+ )
+ parser.add_argument(
+ "-r",
+ "--reverse",
+ dest="reverse",
+ action="store_true",
+ default=False,
+ help="reverse. Go from descriptor to method-declaration")
+ parser.add_argument("method", help="what to change", nargs="*")
+ args = parser.parse_args()
+ if args.method != []:
+ inputs = iter(args.method)
+ else:
+ inputs = GetStdinLineIter()
+ for name in inputs:
+ if args.reverse:
+ print(DescriptorToName(name))
+ else:
+ print(NameToDescriptor(name))
+
+
+if __name__ == "__main__":
+ main()