#!/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()