Igor Murashkin | 5995a8e | 2017-06-27 17:20:50 -0700 | [diff] [blame] | 1 | #!/usr/bin/python3 |
| 2 | # Copyright (C) 2017 The Android Open Source Project |
| 3 | # |
| 4 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | # you may not use this file except in compliance with the License. |
| 6 | # You may obtain a copy of the License at |
| 7 | # |
| 8 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | # |
| 10 | # Unless required by applicable law or agreed to in writing, software |
| 11 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. |
| 15 | |
| 16 | # |
| 17 | # Generates the src/art/Test988Intrinsics.java file. |
| 18 | # Re-run this every time art/compiler/intrinics_list.h is modified. |
| 19 | # |
| 20 | # $> python3.4 gen_srcs.py > src/art/Test988Intrinsics.java |
| 21 | # |
| 22 | |
| 23 | import argparse |
| 24 | import os |
| 25 | import re |
| 26 | import collections |
| 27 | import sys |
| 28 | |
| 29 | from string import Template |
| 30 | |
Orion Hodson | 26ef34c | 2017-11-01 13:32:41 +0000 | [diff] [blame^] | 31 | # Relative path to art/runtime/intrinsics_list.h |
| 32 | INTRINSICS_LIST_H = os.path.dirname(os.path.realpath(__file__)) + "/../../runtime/intrinsics_list.h" |
Igor Murashkin | 5995a8e | 2017-06-27 17:20:50 -0700 | [diff] [blame] | 33 | |
| 34 | # Macro parameter index to V(). Negative means from the end. |
| 35 | IDX_STATIC_OR_VIRTUAL = 1 |
| 36 | IDX_SIGNATURE = -1 |
| 37 | IDX_METHOD_NAME = -2 |
| 38 | IDX_CLASS_NAME = -3 |
| 39 | |
| 40 | # Exclude all hidden API. |
Orion Hodson | 4a4610a | 2017-09-28 16:57:55 +0100 | [diff] [blame] | 41 | KLASS_BLACK_LIST = ['sun.misc.Unsafe', 'libcore.io.Memory', 'java.lang.StringFactory', |
| 42 | 'java.lang.invoke.VarHandle' ] # TODO(b/65872996): Enable when VarHandle is visible. |
Igor Murashkin | 5995a8e | 2017-06-27 17:20:50 -0700 | [diff] [blame] | 43 | METHOD_BLACK_LIST = [('java.lang.ref.Reference', 'getReferent'), |
| 44 | ('java.lang.String', 'getCharsNoCheck'), |
| 45 | ('java.lang.System', 'arraycopy')] # arraycopy has a manual test. |
| 46 | |
| 47 | # When testing a virtual function, it needs to operate on an instance. |
| 48 | # These instances will be created with the following values, |
| 49 | # otherwise a default 'new T()' is used. |
| 50 | KLASS_INSTANCE_INITIALIZERS = { |
| 51 | 'java.lang.String' : '"some large string"', |
| 52 | 'java.lang.StringBuffer' : 'new java.lang.StringBuffer("some large string buffer")', |
| 53 | 'java.lang.StringBuilder' : 'new java.lang.StringBuilder("some large string builder")', |
| 54 | 'java.lang.ref.Reference' : 'new java.lang.ref.WeakReference(new Object())' |
| 55 | }; |
| 56 | |
| 57 | OUTPUT_TPL = Template(""" |
| 58 | /* |
| 59 | * Copyright (C) 2017 The Android Open Source Project |
| 60 | * |
| 61 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 62 | * you may not use this file except in compliance with the License. |
| 63 | * You may obtain a copy of the License at |
| 64 | * |
| 65 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 66 | * |
| 67 | * Unless required by applicable law or agreed to in writing, software |
| 68 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 69 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 70 | * See the License for the specific language governing permissions and |
| 71 | * limitations under the License. |
| 72 | */ |
| 73 | |
| 74 | // AUTO-GENENERATED by gen_srcs.py: DO NOT EDIT HERE DIRECTLY. |
| 75 | // |
| 76 | // $$> python3.4 gen_srcs.py > src/art/Test988Intrinsics.java |
| 77 | // |
| 78 | // RUN ABOVE COMMAND TO REGENERATE THIS FILE. |
| 79 | |
| 80 | package art; |
| 81 | |
| 82 | class Test988Intrinsics { |
| 83 | // Pre-initialize *all* instance variables used so that their constructors are not in the trace. |
| 84 | $static_fields |
| 85 | |
| 86 | static void initialize() { |
| 87 | // Ensure all static variables are initialized. |
| 88 | // In addition, pre-load classes here so that we don't see diverging class loading traces. |
| 89 | $initialize_classes |
| 90 | } |
| 91 | |
| 92 | static void test() { |
Orion Hodson | 26ef34c | 2017-11-01 13:32:41 +0000 | [diff] [blame^] | 93 | // Call each intrinsic from art/runtime/intrinsics_list.h to make sure they are traced. |
Igor Murashkin | 5995a8e | 2017-06-27 17:20:50 -0700 | [diff] [blame] | 94 | $test_body |
| 95 | } |
| 96 | } |
| 97 | """) |
| 98 | |
| 99 | JNI_TYPES = { |
| 100 | 'Z' : 'boolean', |
| 101 | 'B' : 'byte', |
| 102 | 'C' : 'char', |
| 103 | 'S' : 'short', |
| 104 | 'I' : 'int', |
| 105 | 'J' : 'long', |
| 106 | 'F' : 'float', |
| 107 | 'D' : 'double', |
| 108 | 'L' : 'object' |
| 109 | }; |
| 110 | |
| 111 | debug_printing_enabled = False |
| 112 | |
| 113 | def debug_print(x): |
| 114 | if debug_printing_enabled: |
| 115 | print(x, file=sys.stderr) |
| 116 | |
| 117 | # Parse JNI sig into a list, e.g. "II" -> ['I', 'I'], '[[IJ' -> ['[[I', 'J'], etc. |
| 118 | def sig_to_parameter_type_list(sig): |
| 119 | sig = re.sub(r'[(](.*)[)].*', r'\1', sig) |
| 120 | |
| 121 | lst = [] |
| 122 | obj = "" |
| 123 | is_obj = False |
| 124 | is_array = False |
| 125 | for x in sig: |
| 126 | if is_obj: |
| 127 | obj = obj + x |
| 128 | if x == ";": |
| 129 | is_obj = False |
| 130 | lst.append(obj) |
| 131 | obj = "" |
| 132 | elif is_array: |
| 133 | obj = obj + x |
| 134 | if x != "[": |
| 135 | is_array = False |
| 136 | lst.append(obj) |
| 137 | obj = "" |
| 138 | else: |
| 139 | if x == "[": |
| 140 | obj = "[" |
| 141 | is_array = True |
| 142 | elif x == "L": |
| 143 | obj = "L" |
| 144 | is_obj = True |
| 145 | else: |
| 146 | lst.append(x) |
| 147 | |
| 148 | return lst |
| 149 | |
| 150 | # Convert a single JNI descriptor into a pretty java name, e.g. "[I" -> "int[]", etc. |
| 151 | def javafy_name(kls_name): |
| 152 | if kls_name.startswith("L"): |
| 153 | kls_name = kls_name.lstrip("L").rstrip(";") |
| 154 | return kls_name.replace("/", ".") |
| 155 | elif kls_name.startswith("["): |
| 156 | array_count = kls_name.count("[") |
| 157 | non_array = javafy_name(kls_name.lstrip("[")) |
| 158 | return non_array + ("[]" * array_count) |
| 159 | |
| 160 | return JNI_TYPES.get(kls_name, kls_name) |
| 161 | |
| 162 | def extract_staticness(static_or_virtual): |
| 163 | if static_or_virtual == "kStatic": |
| 164 | return 'static' |
| 165 | return 'virtual' # kVirtual, kDirect |
| 166 | |
| 167 | class MethodInfo: |
| 168 | def __init__(self, staticness, pretty_params, method, kls): |
| 169 | # 'virtual' or 'static' |
| 170 | self.staticness = staticness |
| 171 | # list of e.g. ['int', 'double', 'java.lang.String'] etc |
| 172 | self.parameters = pretty_params |
| 173 | # e.g. 'toString' |
| 174 | self.method_name = method |
| 175 | # e.g. 'java.lang.String' |
| 176 | self.klass = kls |
| 177 | |
| 178 | def __str__(self): |
| 179 | return "MethodInfo " + str(self.__dict__) |
| 180 | |
| 181 | def dummy_parameters(self): |
| 182 | dummy_values = { |
| 183 | 'boolean' : 'false', |
| 184 | 'byte' : '(byte)0', |
| 185 | 'char' : "'x'", |
| 186 | 'short' : '(short)0', |
| 187 | 'int' : '0', |
| 188 | 'long' : '0L', |
| 189 | 'float' : '0.0f', |
| 190 | 'double' : '0.0' |
| 191 | } |
| 192 | |
| 193 | def object_dummy(name): |
| 194 | if name == "java.lang.String": |
| 195 | return '"hello"' |
| 196 | else: |
| 197 | return "(%s)null" %(name) |
| 198 | return [ dummy_values.get(param, object_dummy(param)) for param in self.parameters ] |
| 199 | |
| 200 | def dummy_instance_value(self): |
| 201 | return KLASS_INSTANCE_INITIALIZERS.get(self.klass, 'new %s()' %(self.klass)) |
| 202 | |
| 203 | def is_blacklisted(self): |
| 204 | for blk in KLASS_BLACK_LIST: |
| 205 | if self.klass.startswith(blk): |
| 206 | return True |
| 207 | |
| 208 | return (self.klass, self.method_name) in METHOD_BLACK_LIST |
| 209 | |
| 210 | # parse the V(...) \ list of items into a MethodInfo |
| 211 | def parse_method_info(items): |
| 212 | def get_item(idx): |
| 213 | return items[idx].strip().strip("\"") |
| 214 | |
| 215 | staticness = get_item(IDX_STATIC_OR_VIRTUAL) |
| 216 | sig = get_item(IDX_SIGNATURE) |
| 217 | method = get_item(IDX_METHOD_NAME) |
| 218 | kls = get_item(IDX_CLASS_NAME) |
| 219 | |
| 220 | debug_print ((sig, method, kls)) |
| 221 | |
| 222 | staticness = extract_staticness(staticness) |
| 223 | kls = javafy_name(kls) |
| 224 | param_types = sig_to_parameter_type_list(sig) |
| 225 | pretty_params = param_types |
| 226 | pretty_params = [javafy_name(i) for i in param_types] |
| 227 | |
| 228 | return MethodInfo(staticness, pretty_params, method, kls) |
| 229 | |
| 230 | # parse a line containing ' V(...)' into a MethodInfo |
| 231 | def parse_line(line): |
| 232 | line = line.strip() |
| 233 | if not line.startswith("V("): |
| 234 | return None |
| 235 | |
| 236 | line = re.sub(r'V[(](.*)[)]', r'\1', line) |
| 237 | debug_print(line) |
| 238 | |
| 239 | items = line.split(",") |
| 240 | |
| 241 | method_info = parse_method_info(items) |
| 242 | return method_info |
| 243 | |
| 244 | # Generate all the MethodInfo that we parse from intrinsics_list.h |
| 245 | def parse_all_method_infos(): |
| 246 | with open(INTRINSICS_LIST_H) as f: |
| 247 | for line in f: |
| 248 | s = parse_line(line) |
| 249 | if s is not None: |
| 250 | yield s |
| 251 | |
| 252 | # Format a receiver name. For statics, it's the class name, for receivers, it's an instance variable |
| 253 | def format_receiver_name(method_info): |
| 254 | receiver = method_info.klass |
| 255 | if method_info.staticness == 'virtual': |
| 256 | receiver = "instance_" + method_info.klass.replace(".", "_") |
| 257 | return receiver |
| 258 | |
| 259 | # Format a dummy call with dummy method parameters to the requested method. |
| 260 | def format_call_to(method_info): |
| 261 | dummy_args = ", ".join(method_info.dummy_parameters()) |
| 262 | receiver = format_receiver_name(method_info) |
| 263 | |
| 264 | return ("%s.%s(%s);" %(receiver, method_info.method_name, dummy_args)) |
| 265 | |
| 266 | # Format a static variable with an instance that could be used as the receiver |
| 267 | # (or None for non-static methods). |
| 268 | def format_instance_variable(method_info): |
| 269 | if method_info.staticness == 'static': |
| 270 | return None |
| 271 | return "static %s %s = %s;" %(method_info.klass, format_receiver_name(method_info), method_info.dummy_instance_value()) |
| 272 | |
| 273 | def format_initialize_klass(method_info): |
| 274 | return "%s.class.toString();" %(method_info.klass) |
| 275 | |
| 276 | def indent_list(lst, indent): |
| 277 | return [' ' * indent + i for i in lst] |
| 278 | |
| 279 | def main(): |
| 280 | global debug_printing_enabled |
| 281 | parser = argparse.ArgumentParser(description='Generate art/test/988-method-trace/src/art/Test988Intrinsics.java') |
| 282 | parser.add_argument('-d', '--debug', action='store_true', dest='debug', help='Print extra debugging information to stderr.') |
| 283 | parser.add_argument('output_file', nargs='?', metavar='<output-file>', default=sys.stdout, type=argparse.FileType('w'), help='Destination file to write to (default: stdout).') |
| 284 | args = parser.parse_args() |
| 285 | |
| 286 | debug_printing_enabled = args.debug |
| 287 | |
| 288 | ##### |
| 289 | |
| 290 | call_str_list = [] |
| 291 | instance_variable_dict = collections.OrderedDict() |
| 292 | initialize_klass_dict = collections.OrderedDict() |
| 293 | for i in parse_all_method_infos(): |
| 294 | debug_print(i) |
| 295 | if i.is_blacklisted(): |
| 296 | debug_print("Blacklisted: " + str(i)) |
| 297 | continue |
| 298 | |
| 299 | call_str = format_call_to(i) |
| 300 | debug_print(call_str) |
| 301 | |
| 302 | call_str_list.append(call_str) |
| 303 | |
| 304 | instance_variable = format_instance_variable(i) |
| 305 | if instance_variable is not None: |
| 306 | debug_print(instance_variable) |
| 307 | instance_variable_dict[i.klass] = instance_variable |
| 308 | |
| 309 | initialize_klass_dict[i.klass] = format_initialize_klass(i) |
| 310 | |
| 311 | static_fields = indent_list([ value for (key, value) in instance_variable_dict.items() ], 2) |
| 312 | test_body = indent_list(call_str_list, 4) |
| 313 | initialize_classes = indent_list([ value for (key, value) in initialize_klass_dict.items() ], 4) |
| 314 | |
| 315 | print(OUTPUT_TPL.substitute(static_fields="\n".join(static_fields), |
| 316 | test_body="\n".join(test_body), |
| 317 | initialize_classes="\n".join(initialize_classes)). |
| 318 | strip("\n"), \ |
| 319 | file=args.output_file) |
| 320 | |
| 321 | if __name__ == '__main__': |
| 322 | main() |