xref: /aosp_15_r20/external/cronet/third_party/jni_zero/codegen/called_by_native_header.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1# Copyright 2024 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4"""Codegen for calling Java methods from C++."""
5
6from codegen import convert_type
7from codegen import header_common
8import common
9
10
11def constants_enums(java_class, constant_fields):
12  if not constant_fields:
13    return ''
14  sb = common.StringBuilder()
15  sb(f'// Constants\n')
16  sb(f'enum Java_{java_class.name}_constant_fields {{\n')
17  with sb.indent(2):
18    for c in constant_fields:
19      sb(f'{c.name} = {c.value},\n')
20  sb('};\n\n')
21  return sb.to_string()
22
23
24def _return_type_cpp(return_type):
25  if ret := return_type.converted_type():
26    return ret
27  ret = return_type.to_cpp()
28  if not return_type.is_primitive():
29    ret = f'jni_zero::ScopedJavaLocalRef<{ret}>'
30  return ret
31
32
33def _param_type_cpp(java_type):
34  if type_str := java_type.converted_type():
35    if java_type.is_primitive():
36      return type_str
37    return f'{type_str} const&'
38  ret = java_type.to_cpp()
39  if java_type.is_primitive():
40    if ret == 'jint':
41      return 'JniIntWrapper'
42    return ret
43  return f'const jni_zero::JavaRef<{ret}>&'
44
45
46def _param_expression_cpp(param):
47  if converted_type := param.java_type.converted_type():
48    name = f'{param.name}_converted'
49  else:
50    name = param.name
51  if param.java_type.is_primitive():
52    if param.java_type.primitive_name == 'int' and not converted_type:
53      return f'as_jint({name})'
54    return name
55  return f'{name}.obj()'
56
57
58def _jni_function_name(called_by_native):
59  """Maps the types available via env->Call__Method."""
60  if called_by_native.is_constructor:
61    return 'NewObject'
62  if called_by_native.return_type.is_primitive():
63    call = common.capitalize(called_by_native.return_type.primitive_name)
64  else:
65    call = 'Object'
66  if called_by_native.static:
67    call = 'Static' + call
68  return f'Call{call}Method'
69
70
71def _single_method(sb, cbn):
72  java_class = cbn.java_class
73  escaped_name = common.escape_class_name(java_class.full_name_with_slashes)
74  reciever_arg_is_class = cbn.static or cbn.is_constructor
75  if cbn.is_constructor:
76    return_type = cbn.java_class.as_type()
77  else:
78    return_type = cbn.return_type
79  is_void = return_type.is_void()
80
81  if cbn.is_system_class:
82    sb('[[maybe_unused]] ')
83  sb(f'static {_return_type_cpp(return_type)} ')
84  sb(f'Java_{java_class.nested_name}_{cbn.method_id_function_name}')
85  with sb.param_list() as plist:
86    plist.append('JNIEnv* env')
87    if not reciever_arg_is_class:
88      plist.append('const jni_zero::JavaRef<jobject>& obj')
89    plist.extend(f'{_param_type_cpp(p.java_type)} {p.name}' for p in cbn.params)
90
91  with sb.block():
92    sb('static std::atomic<jmethodID> cached_method_id(nullptr);\n')
93    class_accessor = header_common.class_accessor_expression(java_class)
94    receiver_arg = 'clazz' if reciever_arg_is_class else 'obj.obj()'
95
96    sb(f'jclass clazz = {class_accessor};\n')
97    if is_void:
98      sb(f'CHECK_CLAZZ(env, {receiver_arg}, clazz);\n')
99    else:
100      default_value = return_type.to_cpp_default_value()
101      sb(f'CHECK_CLAZZ(env, {receiver_arg}, clazz, {default_value});\n')
102
103    checked_str = 'false' if cbn.unchecked else 'true'
104    method_id_type = 'TYPE_STATIC' if cbn.static else 'TYPE_INSTANCE'
105    sb(f'jni_zero::internal::JniJavaCallContext<{checked_str}> call_context;\n')
106    with sb.statement():
107      sb(f'call_context.Init<jni_zero::MethodID::{method_id_type}>')
108      sb.param_list([
109          'env', 'clazz', f'"{cbn.name}"', f'"{cbn.signature.to_descriptor()}"',
110          '&cached_method_id'
111      ])
112
113    for param in cbn.params:
114      if converted_type := param.java_type.converted_type():
115        convert_type.to_jni_assignment(sb, f'{param.name}_converted',
116                                       param.name, param.java_type)
117
118    if not is_void:
119      return_rvalue = 'ret'
120      sb(f'auto ret = ')
121
122    with sb.statement():
123      sb(f'env->{_jni_function_name(cbn)}')
124      with sb.param_list() as plist:
125        plist += [receiver_arg, 'call_context.method_id()']
126        plist.extend(_param_expression_cpp(p) for p in cbn.params)
127
128    if not is_void:
129      if not return_type.is_primitive():
130        jobject_type = return_type.to_cpp()
131        if jobject_type != 'jobject':
132          return_rvalue = 'ret2'
133          sb(f'{jobject_type} ret2 = static_cast<{jobject_type}>(ret);\n')
134
135      with sb.statement():
136        sb('return ')
137        if return_type.converted_type():
138          convert_type.from_jni_expression(sb, return_rvalue, return_type)
139        elif not return_type.is_primitive():
140          sb(f'jni_zero::ScopedJavaLocalRef<{jobject_type}>(env, '
141             f'{return_rvalue})')
142        else:
143          sb(return_rvalue)
144
145
146def methods(called_by_natives):
147  if not called_by_natives:
148    return ''
149  sb = common.StringBuilder()
150  sb('// Native to Java functions\n')
151  for called_by_native in called_by_natives:
152    _single_method(sb, called_by_native)
153    sb('\n')
154  return sb.to_string()
155