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