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