1# Copyright 2023 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 5import java_types 6import common 7 8 9def _implicit_array_class_param(native, type_resolver): 10 return_type = native.return_type 11 class_name = return_type.to_array_element_type().to_java(type_resolver) 12 ret = class_name + '.class' 13 if native.params: 14 ret = ', ' + ret 15 return ret 16 17 18def Generate(jni_obj, *, gen_jni_class, script_name, per_file_natives=False): 19 proxy_class = java_types.JavaClass( 20 f'{jni_obj.java_class.full_name_with_slashes}Jni') 21 visibility = 'public ' if jni_obj.proxy_visibility == 'public' else '' 22 interface_name = jni_obj.proxy_interface.name_with_dots 23 gen_jni = gen_jni_class.name 24 type_resolver = java_types.TypeResolver(proxy_class) 25 type_resolver.imports = jni_obj.GetClassesToBeImported() 26 27 sb = [] 28 sb.append(f"""\ 29// 30// This file was generated by {script_name} 31// 32package {jni_obj.java_class.class_without_prefix.package_with_dots}; 33 34""") 35 36 import_classes = { 37 'org.jni_zero.CheckDiscard', 'org.jni_zero.JniStaticTestMocker', 38 'org.jni_zero.NativeLibraryLoadedStatus' 39 } 40 if not per_file_natives: 41 import_classes.add(gen_jni_class.full_name_with_dots) 42 43 for c in type_resolver.imports: 44 c = c.class_without_prefix 45 if c.is_nested: 46 # We will refer to all nested classes by OuterClass.InnerClass. We do this 47 # to reduce risk of naming collisions. 48 c = c.get_outer_class() 49 import_classes.add(c.full_name_with_dots) 50 for c in sorted(import_classes): 51 sb.append(f'import {c};\n') 52 53 if not per_file_natives: 54 sb.append('\n@CheckDiscard("crbug.com/993421")') 55 sb.append(f""" 56{visibility}class {proxy_class.name} implements {interface_name} {{ 57 private static {interface_name} testInstance; 58 59 public static final JniStaticTestMocker<{interface_name}> TEST_HOOKS = 60 new JniStaticTestMocker<{interface_name}>() {{ 61 @Override 62 public void setInstanceForTesting({interface_name} instance) {{ 63""") 64 if not per_file_natives: 65 sb.append(f""" if (!{gen_jni}.TESTING_ENABLED) {{ 66 throw new RuntimeException( 67 "Tried to set a JNI mock when mocks aren't enabled!"); 68 }} 69""") 70 71 sb.append(""" testInstance = instance; 72 } 73 }; 74""") 75 76 for native in jni_obj.proxy_natives: 77 call_params = native.params.to_call_str() 78 if native.needs_implicit_array_element_class_param: 79 call_params += _implicit_array_class_param(native, type_resolver) 80 sig_params = native.params.to_java_declaration(type_resolver) 81 return_type_str = native.return_type.to_java(type_resolver) 82 return_prefix = '' 83 if not native.return_type.is_void(): 84 return_prefix = f'return ({return_type_str}) ' 85 86 if per_file_natives: 87 method_fqn = f'native{common.capitalize(native.name)}' 88 else: 89 method_fqn = f'{gen_jni}.{native.proxy_name}' 90 91 sb.append(f""" 92 @Override 93 public {return_type_str} {native.name}({sig_params}) {{ 94""") 95 if native.first_param_cpp_type: 96 sb.append(f"""\ 97 assert {native.params[0].name} != 0; 98""") 99 sb.append(f"""\ 100 {return_prefix}{method_fqn}({call_params}); 101 }} 102""") 103 104 if per_file_natives: 105 proxy_sig_params = native.proxy_params.to_java_declaration() 106 proxy_return_type = native.proxy_return_type.to_java() 107 sb.append( 108 f' private static native {proxy_return_type} {method_fqn}({proxy_sig_params});\n' 109 ) 110 111 sb.append(f""" 112 public static {interface_name} get() {{""") 113 if not per_file_natives: 114 sb.append(f""" 115 if ({gen_jni}.TESTING_ENABLED) {{ 116 if (testInstance != null) {{ 117 return testInstance; 118 }} 119 if ({gen_jni}.REQUIRE_MOCK) {{ 120 throw new UnsupportedOperationException( 121 "No mock found for the native implementation of {interface_name}. " 122 + "The current configuration requires implementations be mocked."); 123 }} 124 }}""") 125 sb.append(f""" 126 NativeLibraryLoadedStatus.checkLoaded(); 127 return new {proxy_class.name}(); 128 }} 129}} 130""") 131 return ''.join(sb) 132