blob: 19e866362f3d3b1f3567fab08fceeb57db907de8 [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright (C) 2021 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.
#
import argparse
from dataclasses import dataclass
from pathlib import Path
import re
import sys
from typing import List, Optional
SIMPLEPERF_DIR = Path(__file__).absolute().parent
AOSP_DIR = SIMPLEPERF_DIR.parents[2]
ART_NATIVE_METHOD_DIR = AOSP_DIR / 'art' / 'runtime' / 'native'
OUTPUT_FILE = SIMPLEPERF_DIR / 'art_jni_method_table.h'
def get_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="generate art jni methods")
parser.add_argument('--check-only', action='store_true',
help='only check if the art jni methods have changed')
return parser.parse_args()
@dataclass
class ArtJniMethod:
# like java.long.reflect.Method
class_name: str
# like invoke
java_method_name: str
# like Method_invoke
native_method_name: str
class ArtJniMethodParser:
def __init__(self):
self.class_name_pattern = re.compile(r'REGISTER_NATIVE_METHODS\(\"(.+?)\"\)')
# from libnativehelper/include_platform_header_only/nativehelper/jni_macros.h
self.method_patterns = []
self.overload_method_patterns = []
for name in ['NATIVE_METHOD', 'FAST_NATIVE_METHOD', 'CRITICAL_NATIVE_METHOD']:
method_s = r'\(\s*(\w+)\s*,\s*(\w+)'
self.method_patterns.append(re.compile(r'\s+' + name + method_s))
self.method_patterns.append(re.compile(r'\s+' + name + 'AUTOSIG' + method_s))
overload_s = r'\(\s*(\w+)\s*,\s*(\w+)\s*,[^,]*,\s*(\w+)'
self.overload_method_patterns.append(re.compile(r'\s+OVERLOADED_' + name + overload_s))
self.static_function_pattern = re.compile(r'static\s+\w+\s+(\w+)\(')
self.file_path = None
def parse_methods(self, file_path: Path) -> List[ArtJniMethod]:
self.file_path = file_path
text = file_path.read_text()
class_name = self._get_class_name(text)
if not class_name:
return []
methods = self._get_methods(text, class_name)
self._check_methods(text, methods)
return methods
def _get_class_name(self, text: str) -> Optional[str]:
""" Return class name like "dalvik.system.BaseDexClassLoader". """
m = self.class_name_pattern.search(text)
if not m:
return None
class_name = m.group(1)
assert self.class_name_pattern.search(text[m.end():]) is None
return class_name.replace('/', '.')
def _get_methods(self, text: str, class_name: str) -> List[ArtJniMethod]:
class_base_name = class_name[class_name.rfind('.') + 1:]
methods = []
for p in self.method_patterns:
for m in p.finditer(text):
assert class_base_name == m.group(1)
methods.append(
ArtJniMethod(
class_name, m.group(2),
class_base_name + '_' + m.group(2)))
for p in self.overload_method_patterns:
for m in p.finditer(text):
assert class_base_name == m.group(1)
methods.append(
ArtJniMethod(
class_name, m.group(2),
class_base_name + '_' + m.group(3)))
return methods
def _check_methods(self, text: str, methods: List[ArtJniMethod]):
static_function_names = set()
for m in self.static_function_pattern.finditer(text):
static_function_names.add(m.group(1))
for method in methods:
assert method.native_method_name in static_function_names, (
"%s isn't a static function in %s" % (method.native_method_name, self.file_path))
def collect_art_jni_methods() -> List[ArtJniMethod]:
methods = []
parser = ArtJniMethodParser()
for file_path in ART_NATIVE_METHOD_DIR.iterdir():
methods += parser.parse_methods(file_path)
return methods
def art_jni_methods_to_str(methods: List[ArtJniMethod]):
lines = ['// This file is generated by generate_art_jni_method_table.py.',
'// clang-format off']
for method in methods:
lines.append(
'ART_JNI_METHOD("%s", "%s")' %
(method.class_name + '.' + method.java_method_name,
"art::" + method.native_method_name))
return '\n'.join(lines)
def main() -> bool:
args = get_args()
methods = collect_art_jni_methods()
text = art_jni_methods_to_str(methods)
if args.check_only:
if text != OUTPUT_FILE.read_text():
print('ART native methods have changed')
return False
return True
OUTPUT_FILE.write_text(text)
return True
if __name__ == '__main__':
sys.exit(0 if main() else 1)