1*6777b538SAndroid Build Coastguard Worker# Copyright 2023 The Chromium Authors 2*6777b538SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 3*6777b538SAndroid Build Coastguard Worker# found in the LICENSE file. 4*6777b538SAndroid Build Coastguard Workerimport dataclasses 5*6777b538SAndroid Build Coastguard Workerimport os 6*6777b538SAndroid Build Coastguard Workerimport re 7*6777b538SAndroid Build Coastguard Workerfrom typing import List 8*6777b538SAndroid Build Coastguard Workerfrom typing import Optional 9*6777b538SAndroid Build Coastguard Worker 10*6777b538SAndroid Build Coastguard Workerimport java_types 11*6777b538SAndroid Build Coastguard Worker 12*6777b538SAndroid Build Coastguard Worker_MODIFIER_KEYWORDS = (r'(?:(?:' + '|'.join([ 13*6777b538SAndroid Build Coastguard Worker 'abstract', 14*6777b538SAndroid Build Coastguard Worker 'default', 15*6777b538SAndroid Build Coastguard Worker 'final', 16*6777b538SAndroid Build Coastguard Worker 'native', 17*6777b538SAndroid Build Coastguard Worker 'private', 18*6777b538SAndroid Build Coastguard Worker 'protected', 19*6777b538SAndroid Build Coastguard Worker 'public', 20*6777b538SAndroid Build Coastguard Worker 'static', 21*6777b538SAndroid Build Coastguard Worker 'synchronized', 22*6777b538SAndroid Build Coastguard Worker]) + r')\s+)*') 23*6777b538SAndroid Build Coastguard Worker 24*6777b538SAndroid Build Coastguard Worker 25*6777b538SAndroid Build Coastguard Workerclass ParseError(Exception): 26*6777b538SAndroid Build Coastguard Worker suffix = '' 27*6777b538SAndroid Build Coastguard Worker 28*6777b538SAndroid Build Coastguard Worker def __str__(self): 29*6777b538SAndroid Build Coastguard Worker return super().__str__() + self.suffix 30*6777b538SAndroid Build Coastguard Worker 31*6777b538SAndroid Build Coastguard Worker 32*6777b538SAndroid Build Coastguard Worker@dataclasses.dataclass(order=True) 33*6777b538SAndroid Build Coastguard Workerclass ParsedNative: 34*6777b538SAndroid Build Coastguard Worker name: str 35*6777b538SAndroid Build Coastguard Worker signature: java_types.JavaSignature 36*6777b538SAndroid Build Coastguard Worker native_class_name: str 37*6777b538SAndroid Build Coastguard Worker static: bool = False 38*6777b538SAndroid Build Coastguard Worker 39*6777b538SAndroid Build Coastguard Worker 40*6777b538SAndroid Build Coastguard Worker@dataclasses.dataclass(order=True) 41*6777b538SAndroid Build Coastguard Workerclass ParsedCalledByNative: 42*6777b538SAndroid Build Coastguard Worker java_class: java_types.JavaClass 43*6777b538SAndroid Build Coastguard Worker name: str 44*6777b538SAndroid Build Coastguard Worker signature: java_types.JavaSignature 45*6777b538SAndroid Build Coastguard Worker static: bool 46*6777b538SAndroid Build Coastguard Worker unchecked: bool = False 47*6777b538SAndroid Build Coastguard Worker 48*6777b538SAndroid Build Coastguard Worker 49*6777b538SAndroid Build Coastguard Worker@dataclasses.dataclass(order=True) 50*6777b538SAndroid Build Coastguard Workerclass ParsedConstantField(object): 51*6777b538SAndroid Build Coastguard Worker name: str 52*6777b538SAndroid Build Coastguard Worker value: str 53*6777b538SAndroid Build Coastguard Worker 54*6777b538SAndroid Build Coastguard Worker 55*6777b538SAndroid Build Coastguard Worker@dataclasses.dataclass 56*6777b538SAndroid Build Coastguard Workerclass ParsedFile: 57*6777b538SAndroid Build Coastguard Worker filename: str 58*6777b538SAndroid Build Coastguard Worker type_resolver: java_types.TypeResolver 59*6777b538SAndroid Build Coastguard Worker proxy_methods: List[ParsedNative] 60*6777b538SAndroid Build Coastguard Worker non_proxy_methods: List[ParsedNative] 61*6777b538SAndroid Build Coastguard Worker called_by_natives: List[ParsedCalledByNative] 62*6777b538SAndroid Build Coastguard Worker constant_fields: List[ParsedConstantField] 63*6777b538SAndroid Build Coastguard Worker proxy_interface: Optional[java_types.JavaClass] = None 64*6777b538SAndroid Build Coastguard Worker proxy_visibility: Optional[str] = None 65*6777b538SAndroid Build Coastguard Worker module_name: Optional[str] = None # E.g. @NativeMethods("module_name") 66*6777b538SAndroid Build Coastguard Worker jni_namespace: Optional[str] = None # E.g. @JNINamespace("content") 67*6777b538SAndroid Build Coastguard Worker 68*6777b538SAndroid Build Coastguard Worker 69*6777b538SAndroid Build Coastguard Worker@dataclasses.dataclass 70*6777b538SAndroid Build Coastguard Workerclass _ParsedProxyNatives: 71*6777b538SAndroid Build Coastguard Worker interface_name: str 72*6777b538SAndroid Build Coastguard Worker visibility: str 73*6777b538SAndroid Build Coastguard Worker module_name: str 74*6777b538SAndroid Build Coastguard Worker methods: List[ParsedNative] 75*6777b538SAndroid Build Coastguard Worker 76*6777b538SAndroid Build Coastguard Worker 77*6777b538SAndroid Build Coastguard Worker# Match single line comments, multiline comments, character literals, and 78*6777b538SAndroid Build Coastguard Worker# double-quoted strings. 79*6777b538SAndroid Build Coastguard Worker_COMMENT_REMOVER_REGEX = re.compile( 80*6777b538SAndroid Build Coastguard Worker r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"', 81*6777b538SAndroid Build Coastguard Worker re.DOTALL | re.MULTILINE) 82*6777b538SAndroid Build Coastguard Worker 83*6777b538SAndroid Build Coastguard Worker 84*6777b538SAndroid Build Coastguard Workerdef _remove_comments(contents): 85*6777b538SAndroid Build Coastguard Worker # We need to support both inline and block comments, and we need to handle 86*6777b538SAndroid Build Coastguard Worker # strings that contain '//' or '/*'. 87*6777b538SAndroid Build Coastguard Worker def replacer(match): 88*6777b538SAndroid Build Coastguard Worker # Replace matches that are comments with nothing; return literals/strings 89*6777b538SAndroid Build Coastguard Worker # unchanged. 90*6777b538SAndroid Build Coastguard Worker s = match.group(0) 91*6777b538SAndroid Build Coastguard Worker if s.startswith('/'): 92*6777b538SAndroid Build Coastguard Worker return '' 93*6777b538SAndroid Build Coastguard Worker else: 94*6777b538SAndroid Build Coastguard Worker return s 95*6777b538SAndroid Build Coastguard Worker 96*6777b538SAndroid Build Coastguard Worker return _COMMENT_REMOVER_REGEX.sub(replacer, contents) 97*6777b538SAndroid Build Coastguard Worker 98*6777b538SAndroid Build Coastguard Worker 99*6777b538SAndroid Build Coastguard Worker# Remove everything between and including <> except at the end of a string, e.g. 100*6777b538SAndroid Build Coastguard Worker# @JniType("std::vector<int>") 101*6777b538SAndroid Build Coastguard Worker# This will also break lines with comparison operators, but we don't care. 102*6777b538SAndroid Build Coastguard Worker_GENERICS_REGEX = re.compile(r'<[^<>\n]*>(?!>*")') 103*6777b538SAndroid Build Coastguard Worker 104*6777b538SAndroid Build Coastguard Worker 105*6777b538SAndroid Build Coastguard Workerdef _remove_generics(value): 106*6777b538SAndroid Build Coastguard Worker """Strips Java generics from a string.""" 107*6777b538SAndroid Build Coastguard Worker while True: 108*6777b538SAndroid Build Coastguard Worker ret = _GENERICS_REGEX.sub('', value) 109*6777b538SAndroid Build Coastguard Worker if len(ret) == len(value): 110*6777b538SAndroid Build Coastguard Worker return ret 111*6777b538SAndroid Build Coastguard Worker value = ret 112*6777b538SAndroid Build Coastguard Worker 113*6777b538SAndroid Build Coastguard Worker 114*6777b538SAndroid Build Coastguard Worker_PACKAGE_REGEX = re.compile('^package\s+(\S+?);', flags=re.MULTILINE) 115*6777b538SAndroid Build Coastguard Worker 116*6777b538SAndroid Build Coastguard Worker 117*6777b538SAndroid Build Coastguard Workerdef _parse_package(contents): 118*6777b538SAndroid Build Coastguard Worker match = _PACKAGE_REGEX.search(contents) 119*6777b538SAndroid Build Coastguard Worker if not match: 120*6777b538SAndroid Build Coastguard Worker raise ParseError('Unable to find "package" line') 121*6777b538SAndroid Build Coastguard Worker return match.group(1) 122*6777b538SAndroid Build Coastguard Worker 123*6777b538SAndroid Build Coastguard Worker 124*6777b538SAndroid Build Coastguard Worker_CLASSES_REGEX = re.compile( 125*6777b538SAndroid Build Coastguard Worker r'^(.*?)(?:\b(?:public|protected|private)?\b)\s*' 126*6777b538SAndroid Build Coastguard Worker r'(?:\b(?:static|abstract|final|sealed)\s+)*' 127*6777b538SAndroid Build Coastguard Worker r'\b(?:class|interface|enum)\s+(\w+?)\b[^"]*?$', 128*6777b538SAndroid Build Coastguard Worker flags=re.MULTILINE) 129*6777b538SAndroid Build Coastguard Worker 130*6777b538SAndroid Build Coastguard Worker 131*6777b538SAndroid Build Coastguard Worker# Does not handle doubly-nested classes. 132*6777b538SAndroid Build Coastguard Workerdef _parse_java_classes(contents): 133*6777b538SAndroid Build Coastguard Worker package = _parse_package(contents).replace('.', '/') 134*6777b538SAndroid Build Coastguard Worker outer_class = None 135*6777b538SAndroid Build Coastguard Worker nested_classes = [] 136*6777b538SAndroid Build Coastguard Worker for m in _CLASSES_REGEX.finditer(contents): 137*6777b538SAndroid Build Coastguard Worker preamble, class_name = m.groups() 138*6777b538SAndroid Build Coastguard Worker # Ignore annotations like @Foo("contains the words class Bar") 139*6777b538SAndroid Build Coastguard Worker if preamble.count('"') % 2 != 0: 140*6777b538SAndroid Build Coastguard Worker continue 141*6777b538SAndroid Build Coastguard Worker if outer_class is None: 142*6777b538SAndroid Build Coastguard Worker outer_class = java_types.JavaClass(f'{package}/{class_name}') 143*6777b538SAndroid Build Coastguard Worker else: 144*6777b538SAndroid Build Coastguard Worker nested_classes.append(outer_class.make_nested(class_name)) 145*6777b538SAndroid Build Coastguard Worker 146*6777b538SAndroid Build Coastguard Worker if outer_class is None: 147*6777b538SAndroid Build Coastguard Worker raise ParseError('No classes found.') 148*6777b538SAndroid Build Coastguard Worker 149*6777b538SAndroid Build Coastguard Worker return outer_class, nested_classes 150*6777b538SAndroid Build Coastguard Worker 151*6777b538SAndroid Build Coastguard Worker 152*6777b538SAndroid Build Coastguard Worker_ANNOTATION_REGEX = re.compile( 153*6777b538SAndroid Build Coastguard Worker r'@(?P<annotation_name>[\w.]+)(?P<annotation_args>\(\s*(?:[^)]+)\s*\))?\s*') 154*6777b538SAndroid Build Coastguard Worker# Only supports ("foo") 155*6777b538SAndroid Build Coastguard Worker_ANNOTATION_ARGS_REGEX = re.compile( 156*6777b538SAndroid Build Coastguard Worker r'\(\s*"(?P<annotation_value>[^"]*?)"\s*\)\s*') 157*6777b538SAndroid Build Coastguard Worker 158*6777b538SAndroid Build Coastguard Workerdef _parse_annotations(value): 159*6777b538SAndroid Build Coastguard Worker annotations = {} 160*6777b538SAndroid Build Coastguard Worker last_idx = 0 161*6777b538SAndroid Build Coastguard Worker for m in _ANNOTATION_REGEX.finditer(value): 162*6777b538SAndroid Build Coastguard Worker string_value = '' 163*6777b538SAndroid Build Coastguard Worker if match_args := m.group('annotation_args'): 164*6777b538SAndroid Build Coastguard Worker if match_arg_value := _ANNOTATION_ARGS_REGEX.match(match_args): 165*6777b538SAndroid Build Coastguard Worker string_value = match_arg_value.group('annotation_value') 166*6777b538SAndroid Build Coastguard Worker annotations[m.group('annotation_name')] = string_value 167*6777b538SAndroid Build Coastguard Worker last_idx = m.end() 168*6777b538SAndroid Build Coastguard Worker 169*6777b538SAndroid Build Coastguard Worker return annotations, value[last_idx:] 170*6777b538SAndroid Build Coastguard Worker 171*6777b538SAndroid Build Coastguard Worker 172*6777b538SAndroid Build Coastguard Workerdef _parse_type(type_resolver, value): 173*6777b538SAndroid Build Coastguard Worker """Parses a string into a JavaType.""" 174*6777b538SAndroid Build Coastguard Worker annotations, value = _parse_annotations(value) 175*6777b538SAndroid Build Coastguard Worker array_dimensions = 0 176*6777b538SAndroid Build Coastguard Worker while value[-2:] == '[]': 177*6777b538SAndroid Build Coastguard Worker array_dimensions += 1 178*6777b538SAndroid Build Coastguard Worker value = value[:-2] 179*6777b538SAndroid Build Coastguard Worker 180*6777b538SAndroid Build Coastguard Worker if value in java_types.PRIMITIVES: 181*6777b538SAndroid Build Coastguard Worker primitive_name = value 182*6777b538SAndroid Build Coastguard Worker java_class = None 183*6777b538SAndroid Build Coastguard Worker else: 184*6777b538SAndroid Build Coastguard Worker primitive_name = None 185*6777b538SAndroid Build Coastguard Worker java_class = type_resolver.resolve(value) 186*6777b538SAndroid Build Coastguard Worker 187*6777b538SAndroid Build Coastguard Worker return java_types.JavaType(array_dimensions=array_dimensions, 188*6777b538SAndroid Build Coastguard Worker primitive_name=primitive_name, 189*6777b538SAndroid Build Coastguard Worker java_class=java_class, 190*6777b538SAndroid Build Coastguard Worker annotations=annotations) 191*6777b538SAndroid Build Coastguard Worker 192*6777b538SAndroid Build Coastguard Worker 193*6777b538SAndroid Build Coastguard Worker_FINAL_REGEX = re.compile(r'\bfinal\s') 194*6777b538SAndroid Build Coastguard Worker 195*6777b538SAndroid Build Coastguard Worker 196*6777b538SAndroid Build Coastguard Workerdef _parse_param_list(type_resolver, value) -> java_types.JavaParamList: 197*6777b538SAndroid Build Coastguard Worker if not value or value.isspace(): 198*6777b538SAndroid Build Coastguard Worker return java_types.EMPTY_PARAM_LIST 199*6777b538SAndroid Build Coastguard Worker params = [] 200*6777b538SAndroid Build Coastguard Worker value = _FINAL_REGEX.sub('', value) 201*6777b538SAndroid Build Coastguard Worker for param_str in value.split(','): 202*6777b538SAndroid Build Coastguard Worker param_str = param_str.strip() 203*6777b538SAndroid Build Coastguard Worker param_str, _, param_name = param_str.rpartition(' ') 204*6777b538SAndroid Build Coastguard Worker param_str = param_str.rstrip() 205*6777b538SAndroid Build Coastguard Worker 206*6777b538SAndroid Build Coastguard Worker # Handle varargs. 207*6777b538SAndroid Build Coastguard Worker if param_str.endswith('...'): 208*6777b538SAndroid Build Coastguard Worker param_str = param_str[:-3] + '[]' 209*6777b538SAndroid Build Coastguard Worker 210*6777b538SAndroid Build Coastguard Worker param_type = _parse_type(type_resolver, param_str) 211*6777b538SAndroid Build Coastguard Worker params.append(java_types.JavaParam(param_type, param_name)) 212*6777b538SAndroid Build Coastguard Worker 213*6777b538SAndroid Build Coastguard Worker return java_types.JavaParamList(params) 214*6777b538SAndroid Build Coastguard Worker 215*6777b538SAndroid Build Coastguard Worker 216*6777b538SAndroid Build Coastguard Worker_NATIVE_METHODS_INTERFACE_REGEX = re.compile( 217*6777b538SAndroid Build Coastguard Worker r'@NativeMethods(?:\(\s*"(?P<module_name>\w+)"\s*\))?[\S\s]+?' 218*6777b538SAndroid Build Coastguard Worker r'(?P<visibility>public)?\s*\binterface\s*' 219*6777b538SAndroid Build Coastguard Worker r'(?P<interface_name>\w*)\s*{(?P<interface_body>(\s*.*)+?\s*)}') 220*6777b538SAndroid Build Coastguard Worker 221*6777b538SAndroid Build Coastguard Worker_PROXY_NATIVE_REGEX = re.compile(r'\s*(.*?)\s+(\w+)\((.*?)\);', flags=re.DOTALL) 222*6777b538SAndroid Build Coastguard Worker 223*6777b538SAndroid Build Coastguard Worker_PUBLIC_REGEX = re.compile(r'\bpublic\s') 224*6777b538SAndroid Build Coastguard Worker 225*6777b538SAndroid Build Coastguard Worker 226*6777b538SAndroid Build Coastguard Workerdef _parse_proxy_natives(type_resolver, contents): 227*6777b538SAndroid Build Coastguard Worker matches = list(_NATIVE_METHODS_INTERFACE_REGEX.finditer(contents)) 228*6777b538SAndroid Build Coastguard Worker if not matches: 229*6777b538SAndroid Build Coastguard Worker return None 230*6777b538SAndroid Build Coastguard Worker if len(matches) > 1: 231*6777b538SAndroid Build Coastguard Worker raise ParseError( 232*6777b538SAndroid Build Coastguard Worker 'Multiple @NativeMethod interfaces in one class is not supported.') 233*6777b538SAndroid Build Coastguard Worker 234*6777b538SAndroid Build Coastguard Worker match = matches[0] 235*6777b538SAndroid Build Coastguard Worker ret = _ParsedProxyNatives(interface_name=match.group('interface_name'), 236*6777b538SAndroid Build Coastguard Worker visibility=match.group('visibility'), 237*6777b538SAndroid Build Coastguard Worker module_name=match.group('module_name'), 238*6777b538SAndroid Build Coastguard Worker methods=[]) 239*6777b538SAndroid Build Coastguard Worker interface_body = match.group('interface_body') 240*6777b538SAndroid Build Coastguard Worker 241*6777b538SAndroid Build Coastguard Worker for m in _PROXY_NATIVE_REGEX.finditer(interface_body): 242*6777b538SAndroid Build Coastguard Worker preamble, name, params_part = m.groups() 243*6777b538SAndroid Build Coastguard Worker preamble = _PUBLIC_REGEX.sub('', preamble) 244*6777b538SAndroid Build Coastguard Worker annotations, _ = _parse_annotations(preamble) 245*6777b538SAndroid Build Coastguard Worker params = _parse_param_list(type_resolver, params_part) 246*6777b538SAndroid Build Coastguard Worker return_type = _parse_type(type_resolver, preamble) 247*6777b538SAndroid Build Coastguard Worker signature = java_types.JavaSignature.from_params(return_type, params) 248*6777b538SAndroid Build Coastguard Worker ret.methods.append( 249*6777b538SAndroid Build Coastguard Worker ParsedNative( 250*6777b538SAndroid Build Coastguard Worker name=name, 251*6777b538SAndroid Build Coastguard Worker signature=signature, 252*6777b538SAndroid Build Coastguard Worker native_class_name=annotations.get('NativeClassQualifiedName'))) 253*6777b538SAndroid Build Coastguard Worker if not ret.methods: 254*6777b538SAndroid Build Coastguard Worker raise ParseError('Found no methods within @NativeMethod interface.') 255*6777b538SAndroid Build Coastguard Worker ret.methods.sort() 256*6777b538SAndroid Build Coastguard Worker return ret 257*6777b538SAndroid Build Coastguard Worker 258*6777b538SAndroid Build Coastguard Worker 259*6777b538SAndroid Build Coastguard Worker_NON_PROXY_NATIVES_REGEX = re.compile( 260*6777b538SAndroid Build Coastguard Worker r'(@NativeClassQualifiedName' 261*6777b538SAndroid Build Coastguard Worker r'\(\"(?P<native_class_name>\S*?)\"\)\s+)?' 262*6777b538SAndroid Build Coastguard Worker r'(?P<qualifiers>\w+\s\w+|\w+|\s+)\s*native\s+' 263*6777b538SAndroid Build Coastguard Worker r'(?P<return_type>\S*)\s+' 264*6777b538SAndroid Build Coastguard Worker r'(?P<name>native\w+)\((?P<params>.*?)\);', re.DOTALL) 265*6777b538SAndroid Build Coastguard Worker 266*6777b538SAndroid Build Coastguard Worker 267*6777b538SAndroid Build Coastguard Workerdef _parse_non_proxy_natives(type_resolver, contents): 268*6777b538SAndroid Build Coastguard Worker ret = [] 269*6777b538SAndroid Build Coastguard Worker for match in _NON_PROXY_NATIVES_REGEX.finditer(contents): 270*6777b538SAndroid Build Coastguard Worker name = match.group('name').replace('native', '') 271*6777b538SAndroid Build Coastguard Worker return_type = _parse_type(type_resolver, match.group('return_type')) 272*6777b538SAndroid Build Coastguard Worker params = _parse_param_list(type_resolver, match.group('params')) 273*6777b538SAndroid Build Coastguard Worker signature = java_types.JavaSignature.from_params(return_type, params) 274*6777b538SAndroid Build Coastguard Worker native_class_name = match.group('native_class_name') 275*6777b538SAndroid Build Coastguard Worker static = 'static' in match.group('qualifiers') 276*6777b538SAndroid Build Coastguard Worker ret.append( 277*6777b538SAndroid Build Coastguard Worker ParsedNative(name=name, 278*6777b538SAndroid Build Coastguard Worker signature=signature, 279*6777b538SAndroid Build Coastguard Worker native_class_name=native_class_name, 280*6777b538SAndroid Build Coastguard Worker static=static)) 281*6777b538SAndroid Build Coastguard Worker ret.sort() 282*6777b538SAndroid Build Coastguard Worker return ret 283*6777b538SAndroid Build Coastguard Worker 284*6777b538SAndroid Build Coastguard Worker 285*6777b538SAndroid Build Coastguard Worker# Regex to match a string like "@CalledByNative public void foo(int bar)". 286*6777b538SAndroid Build Coastguard Worker_CALLED_BY_NATIVE_REGEX = re.compile( 287*6777b538SAndroid Build Coastguard Worker r'@CalledByNative((?P<Unchecked>(?:Unchecked)?|ForTesting))' 288*6777b538SAndroid Build Coastguard Worker r'(?:\("(?P<annotation_value>.*)"\))?' 289*6777b538SAndroid Build Coastguard Worker r'(?P<method_annotations>(?:\s*@\w+(?:\(.*?\))?)+)?' 290*6777b538SAndroid Build Coastguard Worker r'\s+(?P<modifiers>' + _MODIFIER_KEYWORDS + r')' + 291*6777b538SAndroid Build Coastguard Worker r'(?P<return_type_annotations>(?:\s*@\w+(?:\(.*?\))?)+)?' 292*6777b538SAndroid Build Coastguard Worker r'\s*(?P<return_type>\S*?)' 293*6777b538SAndroid Build Coastguard Worker r'\s*(?P<name>\w+)' 294*6777b538SAndroid Build Coastguard Worker r'\s*\(\s*(?P<params>[^{;]*)\)' 295*6777b538SAndroid Build Coastguard Worker r'\s*(?:throws\s+[^{;]+)?' 296*6777b538SAndroid Build Coastguard Worker r'[{;]') 297*6777b538SAndroid Build Coastguard Worker 298*6777b538SAndroid Build Coastguard Worker 299*6777b538SAndroid Build Coastguard Workerdef _parse_called_by_natives(type_resolver, contents): 300*6777b538SAndroid Build Coastguard Worker ret = [] 301*6777b538SAndroid Build Coastguard Worker for match in _CALLED_BY_NATIVE_REGEX.finditer(contents): 302*6777b538SAndroid Build Coastguard Worker return_type_grp = match.group('return_type') 303*6777b538SAndroid Build Coastguard Worker name = match.group('name') 304*6777b538SAndroid Build Coastguard Worker if return_type_grp: 305*6777b538SAndroid Build Coastguard Worker pre_annotations = match.group('method_annotations') or '' 306*6777b538SAndroid Build Coastguard Worker post_annotations = match.group('return_type_annotations') or '' 307*6777b538SAndroid Build Coastguard Worker # Combine all the annotations before parsing the return type. 308*6777b538SAndroid Build Coastguard Worker return_type_str = str.strip(f'{pre_annotations} {post_annotations}' 309*6777b538SAndroid Build Coastguard Worker f' {return_type_grp}') 310*6777b538SAndroid Build Coastguard Worker return_type = _parse_type(type_resolver, return_type_str) 311*6777b538SAndroid Build Coastguard Worker else: 312*6777b538SAndroid Build Coastguard Worker return_type = java_types.VOID 313*6777b538SAndroid Build Coastguard Worker name = '<init>' 314*6777b538SAndroid Build Coastguard Worker 315*6777b538SAndroid Build Coastguard Worker params = _parse_param_list(type_resolver, match.group('params')) 316*6777b538SAndroid Build Coastguard Worker signature = java_types.JavaSignature.from_params(return_type, params) 317*6777b538SAndroid Build Coastguard Worker inner_class_name = match.group('annotation_value') 318*6777b538SAndroid Build Coastguard Worker java_class = type_resolver.java_class 319*6777b538SAndroid Build Coastguard Worker if inner_class_name: 320*6777b538SAndroid Build Coastguard Worker java_class = java_class.make_nested(inner_class_name) 321*6777b538SAndroid Build Coastguard Worker 322*6777b538SAndroid Build Coastguard Worker ret.append( 323*6777b538SAndroid Build Coastguard Worker ParsedCalledByNative(java_class=java_class, 324*6777b538SAndroid Build Coastguard Worker name=name, 325*6777b538SAndroid Build Coastguard Worker signature=signature, 326*6777b538SAndroid Build Coastguard Worker static='static' in match.group('modifiers'), 327*6777b538SAndroid Build Coastguard Worker unchecked='Unchecked' in match.group('Unchecked'))) 328*6777b538SAndroid Build Coastguard Worker 329*6777b538SAndroid Build Coastguard Worker # Check for any @CalledByNative occurrences that were not matched. 330*6777b538SAndroid Build Coastguard Worker unmatched_lines = _CALLED_BY_NATIVE_REGEX.sub('', contents).splitlines() 331*6777b538SAndroid Build Coastguard Worker for i, line in enumerate(unmatched_lines): 332*6777b538SAndroid Build Coastguard Worker if '@CalledByNative' in line: 333*6777b538SAndroid Build Coastguard Worker context = '\n'.join(unmatched_lines[i:i + 5]) 334*6777b538SAndroid Build Coastguard Worker raise ParseError('Could not parse @CalledByNative method signature:\n' + 335*6777b538SAndroid Build Coastguard Worker context) 336*6777b538SAndroid Build Coastguard Worker 337*6777b538SAndroid Build Coastguard Worker ret.sort() 338*6777b538SAndroid Build Coastguard Worker return ret 339*6777b538SAndroid Build Coastguard Worker 340*6777b538SAndroid Build Coastguard Worker 341*6777b538SAndroid Build Coastguard Worker_IMPORT_REGEX = re.compile(r'^import\s+([^\s*]+);', flags=re.MULTILINE) 342*6777b538SAndroid Build Coastguard Worker_IMPORT_CLASS_NAME_REGEX = re.compile(r'^(.*?)\.([A-Z].*)') 343*6777b538SAndroid Build Coastguard Worker 344*6777b538SAndroid Build Coastguard Worker 345*6777b538SAndroid Build Coastguard Workerdef _parse_imports(contents): 346*6777b538SAndroid Build Coastguard Worker # Regex skips static imports as well as wildcard imports. 347*6777b538SAndroid Build Coastguard Worker names = _IMPORT_REGEX.findall(contents) 348*6777b538SAndroid Build Coastguard Worker for name in names: 349*6777b538SAndroid Build Coastguard Worker m = _IMPORT_CLASS_NAME_REGEX.match(name) 350*6777b538SAndroid Build Coastguard Worker if m: 351*6777b538SAndroid Build Coastguard Worker package, class_name = m.groups() 352*6777b538SAndroid Build Coastguard Worker yield java_types.JavaClass( 353*6777b538SAndroid Build Coastguard Worker package.replace('.', '/') + '/' + class_name.replace('.', '$')) 354*6777b538SAndroid Build Coastguard Worker 355*6777b538SAndroid Build Coastguard Worker 356*6777b538SAndroid Build Coastguard Worker_JNI_NAMESPACE_REGEX = re.compile('@JNINamespace\("(.*?)"\)') 357*6777b538SAndroid Build Coastguard Worker 358*6777b538SAndroid Build Coastguard Worker 359*6777b538SAndroid Build Coastguard Workerdef _parse_jni_namespace(contents): 360*6777b538SAndroid Build Coastguard Worker m = _JNI_NAMESPACE_REGEX.findall(contents) 361*6777b538SAndroid Build Coastguard Worker if not m: 362*6777b538SAndroid Build Coastguard Worker return '' 363*6777b538SAndroid Build Coastguard Worker if len(m) > 1: 364*6777b538SAndroid Build Coastguard Worker raise ParseError('Found multiple @JNINamespace annotations.') 365*6777b538SAndroid Build Coastguard Worker return m[0] 366*6777b538SAndroid Build Coastguard Worker 367*6777b538SAndroid Build Coastguard Worker 368*6777b538SAndroid Build Coastguard Workerdef _do_parse(filename, *, package_prefix): 369*6777b538SAndroid Build Coastguard Worker assert not filename.endswith('.kt'), ( 370*6777b538SAndroid Build Coastguard Worker f'Found {filename}, but Kotlin is not supported by JNI generator.') 371*6777b538SAndroid Build Coastguard Worker with open(filename) as f: 372*6777b538SAndroid Build Coastguard Worker contents = f.read() 373*6777b538SAndroid Build Coastguard Worker contents = _remove_comments(contents) 374*6777b538SAndroid Build Coastguard Worker contents = _remove_generics(contents) 375*6777b538SAndroid Build Coastguard Worker 376*6777b538SAndroid Build Coastguard Worker outer_class, nested_classes = _parse_java_classes(contents) 377*6777b538SAndroid Build Coastguard Worker 378*6777b538SAndroid Build Coastguard Worker expected_name = os.path.splitext(os.path.basename(filename))[0] 379*6777b538SAndroid Build Coastguard Worker if outer_class.name != expected_name: 380*6777b538SAndroid Build Coastguard Worker raise ParseError( 381*6777b538SAndroid Build Coastguard Worker f'Found class "{outer_class.name}" but expected "{expected_name}".') 382*6777b538SAndroid Build Coastguard Worker 383*6777b538SAndroid Build Coastguard Worker if package_prefix: 384*6777b538SAndroid Build Coastguard Worker outer_class = outer_class.make_prefixed(package_prefix) 385*6777b538SAndroid Build Coastguard Worker nested_classes = [c.make_prefixed(package_prefix) for c in nested_classes] 386*6777b538SAndroid Build Coastguard Worker 387*6777b538SAndroid Build Coastguard Worker type_resolver = java_types.TypeResolver(outer_class) 388*6777b538SAndroid Build Coastguard Worker for java_class in _parse_imports(contents): 389*6777b538SAndroid Build Coastguard Worker type_resolver.add_import(java_class) 390*6777b538SAndroid Build Coastguard Worker for java_class in nested_classes: 391*6777b538SAndroid Build Coastguard Worker type_resolver.add_nested_class(java_class) 392*6777b538SAndroid Build Coastguard Worker 393*6777b538SAndroid Build Coastguard Worker parsed_proxy_natives = _parse_proxy_natives(type_resolver, contents) 394*6777b538SAndroid Build Coastguard Worker jni_namespace = _parse_jni_namespace(contents) 395*6777b538SAndroid Build Coastguard Worker 396*6777b538SAndroid Build Coastguard Worker non_proxy_methods = _parse_non_proxy_natives(type_resolver, contents) 397*6777b538SAndroid Build Coastguard Worker called_by_natives = _parse_called_by_natives(type_resolver, contents) 398*6777b538SAndroid Build Coastguard Worker 399*6777b538SAndroid Build Coastguard Worker ret = ParsedFile(filename=filename, 400*6777b538SAndroid Build Coastguard Worker jni_namespace=jni_namespace, 401*6777b538SAndroid Build Coastguard Worker type_resolver=type_resolver, 402*6777b538SAndroid Build Coastguard Worker proxy_methods=[], 403*6777b538SAndroid Build Coastguard Worker non_proxy_methods=non_proxy_methods, 404*6777b538SAndroid Build Coastguard Worker called_by_natives=called_by_natives, 405*6777b538SAndroid Build Coastguard Worker constant_fields=[]) 406*6777b538SAndroid Build Coastguard Worker 407*6777b538SAndroid Build Coastguard Worker if parsed_proxy_natives: 408*6777b538SAndroid Build Coastguard Worker ret.module_name = parsed_proxy_natives.module_name 409*6777b538SAndroid Build Coastguard Worker ret.proxy_interface = outer_class.make_nested( 410*6777b538SAndroid Build Coastguard Worker parsed_proxy_natives.interface_name) 411*6777b538SAndroid Build Coastguard Worker ret.proxy_visibility = parsed_proxy_natives.visibility 412*6777b538SAndroid Build Coastguard Worker ret.proxy_methods = parsed_proxy_natives.methods 413*6777b538SAndroid Build Coastguard Worker 414*6777b538SAndroid Build Coastguard Worker return ret 415*6777b538SAndroid Build Coastguard Worker 416*6777b538SAndroid Build Coastguard Worker 417*6777b538SAndroid Build Coastguard Workerdef parse_java_file(filename, *, package_prefix=None): 418*6777b538SAndroid Build Coastguard Worker try: 419*6777b538SAndroid Build Coastguard Worker return _do_parse(filename, package_prefix=package_prefix) 420*6777b538SAndroid Build Coastguard Worker except ParseError as e: 421*6777b538SAndroid Build Coastguard Worker e.suffix = f' (when parsing {filename})' 422*6777b538SAndroid Build Coastguard Worker raise 423*6777b538SAndroid Build Coastguard Worker 424*6777b538SAndroid Build Coastguard Worker 425*6777b538SAndroid Build Coastguard Worker_JAVAP_CLASS_REGEX = re.compile(r'\b(?:class|interface) (\S+)') 426*6777b538SAndroid Build Coastguard Worker_JAVAP_FINAL_FIELD_REGEX = re.compile( 427*6777b538SAndroid Build Coastguard Worker r'^\s+public static final \S+ (.*?) = (\d+);', flags=re.MULTILINE) 428*6777b538SAndroid Build Coastguard Worker_JAVAP_METHOD_REGEX = re.compile( 429*6777b538SAndroid Build Coastguard Worker rf'^\s*({_MODIFIER_KEYWORDS}).*?(\S+?)\(.*\n\s+descriptor: (.*)', 430*6777b538SAndroid Build Coastguard Worker flags=re.MULTILINE) 431*6777b538SAndroid Build Coastguard Worker 432*6777b538SAndroid Build Coastguard Worker 433*6777b538SAndroid Build Coastguard Workerdef parse_javap(filename, contents): 434*6777b538SAndroid Build Coastguard Worker contents = _remove_generics(contents) 435*6777b538SAndroid Build Coastguard Worker match = _JAVAP_CLASS_REGEX.search(contents) 436*6777b538SAndroid Build Coastguard Worker if not match: 437*6777b538SAndroid Build Coastguard Worker raise ParseError('Could not find java class in javap output') 438*6777b538SAndroid Build Coastguard Worker java_class = java_types.JavaClass(match.group(1).replace('.', '/')) 439*6777b538SAndroid Build Coastguard Worker type_resolver = java_types.TypeResolver(java_class) 440*6777b538SAndroid Build Coastguard Worker 441*6777b538SAndroid Build Coastguard Worker constant_fields = [] 442*6777b538SAndroid Build Coastguard Worker for match in _JAVAP_FINAL_FIELD_REGEX.finditer(contents): 443*6777b538SAndroid Build Coastguard Worker name, value = match.groups() 444*6777b538SAndroid Build Coastguard Worker constant_fields.append(ParsedConstantField(name=name, value=value)) 445*6777b538SAndroid Build Coastguard Worker constant_fields.sort() 446*6777b538SAndroid Build Coastguard Worker 447*6777b538SAndroid Build Coastguard Worker called_by_natives = [] 448*6777b538SAndroid Build Coastguard Worker for match in _JAVAP_METHOD_REGEX.finditer(contents): 449*6777b538SAndroid Build Coastguard Worker modifiers, name, descriptor = match.groups() 450*6777b538SAndroid Build Coastguard Worker if name == java_class.full_name_with_dots: 451*6777b538SAndroid Build Coastguard Worker name = '<init>' 452*6777b538SAndroid Build Coastguard Worker signature = java_types.JavaSignature.from_descriptor(descriptor) 453*6777b538SAndroid Build Coastguard Worker 454*6777b538SAndroid Build Coastguard Worker called_by_natives.append( 455*6777b538SAndroid Build Coastguard Worker ParsedCalledByNative(java_class=java_class, 456*6777b538SAndroid Build Coastguard Worker name=name, 457*6777b538SAndroid Build Coastguard Worker signature=signature, 458*6777b538SAndroid Build Coastguard Worker static='static' in modifiers)) 459*6777b538SAndroid Build Coastguard Worker called_by_natives.sort() 460*6777b538SAndroid Build Coastguard Worker 461*6777b538SAndroid Build Coastguard Worker # Although javac will not allow multiple methods with no args and different 462*6777b538SAndroid Build Coastguard Worker # return types, Class.class has just that, and it breaks with our 463*6777b538SAndroid Build Coastguard Worker # name-mangling logic which assumes this cannot happen. 464*6777b538SAndroid Build Coastguard Worker if java_class.full_name_with_slashes == 'java/lang/Class': 465*6777b538SAndroid Build Coastguard Worker called_by_natives = [ 466*6777b538SAndroid Build Coastguard Worker x for x in called_by_natives if 'TypeDescriptor' not in ( 467*6777b538SAndroid Build Coastguard Worker x.signature.return_type.non_array_full_name_with_slashes) 468*6777b538SAndroid Build Coastguard Worker ] 469*6777b538SAndroid Build Coastguard Worker 470*6777b538SAndroid Build Coastguard Worker return ParsedFile(filename=filename, 471*6777b538SAndroid Build Coastguard Worker type_resolver=type_resolver, 472*6777b538SAndroid Build Coastguard Worker proxy_methods=[], 473*6777b538SAndroid Build Coastguard Worker non_proxy_methods=[], 474*6777b538SAndroid Build Coastguard Worker called_by_natives=called_by_natives, 475*6777b538SAndroid Build Coastguard Worker constant_fields=constant_fields) 476