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