blob: 9d26032edc640aa5cdeadf3b3a961d41f990bb0d [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',
42 'java.lang.invoke.VarHandle' ] # TODO(b/65872996): Enable when VarHandle is visible.
Igor Murashkin5995a8e2017-06-27 17:20:50 -070043METHOD_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.
50KLASS_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
57OUTPUT_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
80package art;
81
82class 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 Hodson26ef34c2017-11-01 13:32:41 +000093 // Call each intrinsic from art/runtime/intrinsics_list.h to make sure they are traced.
Igor Murashkin5995a8e2017-06-27 17:20:50 -070094$test_body
95 }
96}
97""")
98
99JNI_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
111debug_printing_enabled = False
112
113def 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.
118def 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.
151def 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
162def extract_staticness(static_or_virtual):
163 if static_or_virtual == "kStatic":
164 return 'static'
165 return 'virtual' # kVirtual, kDirect
166
167class 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
211def 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
231def 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
245def 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
253def 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.
260def 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).
268def 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
273def format_initialize_klass(method_info):
274 return "%s.class.toString();" %(method_info.klass)
275
276def indent_list(lst, indent):
277 return [' ' * indent + i for i in lst]
278
279def 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
321if __name__ == '__main__':
322 main()