• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python
2#
3# Copyright (C) 2023 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""Parse ABI functions declarations and generate trampolines.
18
19Input files are C++ files with very limited syntax. They can contain function
20declarations and comments only.
21
22Function declaration should not specify parameter names, just parameter types.
23
24Comments should be C++-comments and they should appear before or after function
25declaration but not inside it.
26
27Special comments define attributes of the subsequent declaration:
28- // BERBERIS_CUSTOM_TRAMPOLINE(<name>)
29  This declaration already has a custom hand-coded trampoline <name>;
30
31Unrecognized comment discards all preceding attributes, so if declaration with
32attributes is commented out, its attributes are not attached to the next
33declaration.
34"""
35
36import json
37import sys
38
39
40_ARG = {
41    'JNIEnv *': {
42        'init': '  JNIEnv* arg_{index} = ToHostJNIEnv(arg_env);'
43    },
44    '...': {
45        'init': """\
46  std::vector<jvalue> arg_vector = ConvertVAList(
47    arg_0, arg_{arg_va_method_id}, GuestParamsValues<PFN_callee>(state));
48  jvalue* arg_{index} = &arg_vector[0];""",
49    },
50  # Note: we couldn't teach GuestParams class to distinguish Va_list because on
51  # ARM it's simply a char*, not a distint type.
52    'va_list': {
53        'init': """\
54  std::vector<jvalue> arg_vector = ConvertVAList(
55    arg_0, arg_{arg_va_method_id}, ToGuestAddr(arg_va));
56  jvalue* arg_{index} = &arg_vector[0];""",
57    },
58}
59
60
61def _set_callee(decl):
62  param_types = decl['param_types']
63  # TODO(eaeltsin): introduce attributes to
64  # - indicate this is a virtual method and not a global function
65  # - specify custom thunk
66  # - specify how to convert variable arguments list
67  # instead of the code below.
68  if 'jmethodID' in param_types and 'va_list' in param_types:
69    # NewObjectV, CallObjectMethodV, ...
70    assert decl['name'].endswith('V')
71    decl['callee'] = '(arg_0->functions)->%sA' % decl['name'][:-1]
72    decl['arg_va_method_id'] = param_types.index('jmethodID')
73  elif 'jmethodID' in param_types and '...' in param_types:
74    # NewObject, CallObjectMethod, ...
75    decl['callee'] = '(arg_0->functions)->%sA' % decl['name']
76    decl['arg_va_method_id'] = param_types.index('jmethodID')
77  else:
78    decl['callee'] = '(arg_0->functions)->%s' % decl['name']
79
80
81def _print_jni_call(out, args, decl):
82  # TODO(eaeltsin): implement printing using printf-style LOG_JNI!
83  pass
84
85
86def _print_jni_result(out, res, decl):
87  # TODO(eaeltsin): implement printing using printf-style LOG_JNI!
88  pass
89
90
91def _gen_trampoline(out, decl):
92  _set_callee(decl)
93
94  args = decl['param_types']
95
96  if '...' in args:
97    assert args[-1] == '...'
98    arglist = ['arg_%d' %i for i in range(1, len(args) - 1)]
99  elif 'va_list' in args:
100    assert args[-1] == 'va_list'
101    arglist = ['arg_%d' %i for i in range(1, len(args) - 1)] + ['arg_va']
102  else:
103    arglist = ['arg_%d' %i for i in range(1, len(args))]
104  assert args[0] == 'JNIEnv *'
105  decl['arglist'] = ', '.join(['arg_env'] + arglist)
106
107  print("""
108void DoTrampoline_JNIEnv_{name}(
109    HostCode /* callee */,
110    ProcessState* state) {{
111  using PFN_callee = decltype(std::declval<JNIEnv>().functions->{name});
112  auto [{arglist}] = GuestParamsValues<PFN_callee>(state);""".format(**decl), file=out)
113
114  for i, type in enumerate(args):
115    if type in _ARG:
116      arg = _ARG[type]
117      print(arg['init'].format(index=i, **decl), file=out)
118
119  _print_jni_call(out, args, decl)
120  if decl['return_type'] == 'void':
121    print(' {callee}('.format(**decl), file=out)
122  else:
123    print('  auto&& [ret] = GuestReturnReference<PFN_callee>(state);', file=out)
124    print('  ret = {callee}('.format(**decl), file=out)
125  for i in range(len(args) - 1):
126    print('      arg_%d,' % i, file=out)
127  print('      arg_%d);' % (len(args) - 1), file=out)
128  if decl['return_type'] != 'void':
129    _print_jni_result(out, 'ret', decl)
130
131  print('}', file=out)
132
133
134def main(argv):
135  # Usage: gen_jni_trampolines.py <gen-header> <abi-def>
136  header_name = argv[1]
137  abi_def_name = argv[2]
138
139  with open(abi_def_name) as json_file:
140    decls = json.load(json_file)
141
142  out = open(header_name, 'w')
143
144  for decl in decls:
145    if 'trampoline' not in decl:
146      _gen_trampoline(out, decl)
147
148  print("""
149void WrapJNIEnv(void* jni_env) {
150  HostCode* jni_vtable = *reinterpret_cast<HostCode**>(jni_env);
151  // jni_vtable[0] is NULL
152  // jni_vtable[1] is NULL
153  // jni_vtable[2] is NULL
154  // jni_vtable[3] is NULL""", file=out)
155  for i in range(len(decls)):
156    decl = decls[i]
157    print("""
158  WrapHostFunctionImpl(
159    jni_vtable[%d],
160    DoTrampoline_JNIEnv_%s,
161    "JNIEnv::%s");""" % (4 + i, decl['name'], decl['name']), file=out)
162  print('}', file=out)
163
164
165if __name__ == '__main__':
166  sys.exit(main(sys.argv))
167