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"""Codegen for FooJni.java files.""" 5 6import common 7import java_types 8import proxy 9 10 11class _Context: 12 13 def __init__(self, jni_obj, gen_jni_class, script_name, is_per_file): 14 self.jni_obj = jni_obj 15 self.gen_jni_class = gen_jni_class 16 self.script_name = script_name 17 self.is_per_file = is_per_file 18 19 self.interface_name = jni_obj.proxy_interface.name_with_dots 20 self.proxy_class = java_types.JavaClass( 21 f'{self.jni_obj.java_class.full_name_with_slashes}Jni') 22 self.type_resolver = java_types.TypeResolver(self.proxy_class) 23 imports = jni_obj.GetClassesToBeImported() + [ 24 java_types.JavaClass('org/jni_zero/CheckDiscard'), 25 java_types.JavaClass('org/jni_zero/JniTestInstanceHolder'), 26 java_types.JavaClass('org/jni_zero/NativeLibraryLoadedStatus'), 27 ] 28 if not is_per_file: 29 imports.append(gen_jni_class) 30 self.type_resolver.imports = imports 31 32 33def _implicit_array_class_param(native, type_resolver): 34 return_type = native.return_type 35 class_name = return_type.to_array_element_type().to_java(type_resolver) 36 return class_name + '.class' 37 38 39def _proxy_method(sb, ctx, native, method_fqn): 40 return_type_str = native.return_type.to_java(ctx.type_resolver) 41 sig_params = native.params.to_java_declaration(ctx.type_resolver) 42 43 sb(f""" 44@Override 45public {return_type_str} {native.name}({sig_params})""") 46 with sb.block(): 47 if native.first_param_cpp_type: 48 sb(f'assert {native.params[0].name} != 0;\n') 49 for p in native.params: 50 if not p.java_type.nullable: 51 sb(f'assert {p.name} != null;\n') 52 with sb.statement(): 53 if not native.return_type.is_void(): 54 sb(f'return ({return_type_str}) ') 55 sb(method_fqn) 56 with sb.param_list() as plist: 57 plist.extend(p.name for p in native.params) 58 if native.needs_implicit_array_element_class_param: 59 plist.append(_implicit_array_class_param(native, ctx.type_resolver)) 60 61 62def _native_method(sb, native, name): 63 params = native.proxy_params.to_java_declaration() 64 return_type = native.proxy_return_type.to_java() 65 sb(f'private static native {return_type} {name}({params});\n') 66 67 68def _class_body(sb, ctx): 69 sb(f"""\ 70private static JniTestInstanceHolder sOverride; 71 72public static {ctx.interface_name} get() {{ 73 JniTestInstanceHolder holder = sOverride; 74 if (holder != null && holder.value != null) {{ 75 return ({ctx.interface_name}) holder.value; 76 }} 77 NativeLibraryLoadedStatus.checkLoaded(); 78 return new {ctx.proxy_class.name}(); 79}} 80 81public static void setInstanceForTesting({ctx.interface_name} impl) {{ 82 if (sOverride == null) {{ 83 sOverride = JniTestInstanceHolder.create(); 84 }} 85 sOverride.value = impl; 86}} 87 88""") 89 90 for native in ctx.jni_obj.proxy_natives: 91 if ctx.is_per_file: 92 method_fqn = native.per_file_name 93 _native_method(sb, native, method_fqn) 94 else: 95 method_fqn = f'{ctx.gen_jni_class.name}.{native.proxy_name}' 96 97 _proxy_method(sb, ctx, native, method_fqn) 98 99 100def _imports(sb, ctx): 101 classes = set() 102 for c in ctx.type_resolver.imports: 103 # Since this is Java, the class generated here will go through jarjar 104 # and thus we want to avoid prefixes (with the exception of GEN_JNI). 105 c = c if c is ctx.gen_jni_class else c.class_without_prefix 106 if c.is_nested: 107 # We will refer to all nested classes by OuterClass.InnerClass. We do this 108 # to reduce risk of naming collisions. 109 c = c.get_outer_class() 110 classes.add(c.full_name_with_dots) 111 112 for c in sorted(classes): 113 sb(f'import {c};\n') 114 115 116def Generate(jni_mode, jni_obj, *, gen_jni_class, script_name): 117 ctx = _Context(jni_obj, gen_jni_class, script_name, jni_mode.is_per_file) 118 119 sb = common.StringBuilder() 120 sb(f"""\ 121// 122// This file was generated by {script_name} 123// 124package {jni_obj.java_class.class_without_prefix.package_with_dots}; 125 126""") 127 _imports(sb, ctx) 128 sb('\n') 129 130 visibility = 'public ' if jni_obj.proxy_visibility == 'public' else '' 131 class_name = ctx.proxy_class.name 132 if not ctx.is_per_file: 133 sb('@CheckDiscard("crbug.com/993421")\n') 134 sb(f'{visibility}class {class_name} implements {ctx.interface_name}') 135 with sb.block(): 136 _class_body(sb, ctx) 137 return sb.to_string() 138