# Copyright 2023 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Codegen for FooJni.java files.""" import common import java_types import proxy class _Context: def __init__(self, jni_obj, gen_jni_class, script_name, is_per_file): self.jni_obj = jni_obj self.gen_jni_class = gen_jni_class self.script_name = script_name self.is_per_file = is_per_file self.interface_name = jni_obj.proxy_interface.name_with_dots self.proxy_class = java_types.JavaClass( f'{self.jni_obj.java_class.full_name_with_slashes}Jni') self.type_resolver = java_types.TypeResolver(self.proxy_class) imports = jni_obj.GetClassesToBeImported() + [ java_types.JavaClass('org/jni_zero/CheckDiscard'), java_types.JavaClass('org/jni_zero/JniTestInstanceHolder'), java_types.JavaClass('org/jni_zero/NativeLibraryLoadedStatus'), ] if not is_per_file: imports.append(gen_jni_class) self.type_resolver.imports = imports def _implicit_array_class_param(native, type_resolver): return_type = native.return_type class_name = return_type.to_array_element_type().to_java(type_resolver) return class_name + '.class' def _proxy_method(sb, ctx, native, method_fqn): return_type_str = native.return_type.to_java(ctx.type_resolver) sig_params = native.params.to_java_declaration(ctx.type_resolver) sb(f""" @Override public {return_type_str} {native.name}({sig_params})""") with sb.block(): if native.first_param_cpp_type: sb(f'assert {native.params[0].name} != 0;\n') for p in native.params: if not p.java_type.nullable: sb(f'assert {p.name} != null;\n') with sb.statement(): if not native.return_type.is_void(): sb(f'return ({return_type_str}) ') sb(method_fqn) with sb.param_list() as plist: plist.extend(p.name for p in native.params) if native.needs_implicit_array_element_class_param: plist.append(_implicit_array_class_param(native, ctx.type_resolver)) def _native_method(sb, native, name): params = native.proxy_params.to_java_declaration() return_type = native.proxy_return_type.to_java() sb(f'private static native {return_type} {name}({params});\n') def _class_body(sb, ctx): sb(f"""\ private static JniTestInstanceHolder sOverride; public static {ctx.interface_name} get() {{ JniTestInstanceHolder holder = sOverride; if (holder != null && holder.value != null) {{ return ({ctx.interface_name}) holder.value; }} NativeLibraryLoadedStatus.checkLoaded(); return new {ctx.proxy_class.name}(); }} public static void setInstanceForTesting({ctx.interface_name} impl) {{ if (sOverride == null) {{ sOverride = JniTestInstanceHolder.create(); }} sOverride.value = impl; }} """) for native in ctx.jni_obj.proxy_natives: if ctx.is_per_file: method_fqn = native.per_file_name _native_method(sb, native, method_fqn) else: method_fqn = f'{ctx.gen_jni_class.name}.{native.proxy_name}' _proxy_method(sb, ctx, native, method_fqn) def _imports(sb, ctx): classes = set() for c in ctx.type_resolver.imports: # Since this is Java, the class generated here will go through jarjar # and thus we want to avoid prefixes (with the exception of GEN_JNI). c = c if c is ctx.gen_jni_class else c.class_without_prefix if c.is_nested: # We will refer to all nested classes by OuterClass.InnerClass. We do this # to reduce risk of naming collisions. c = c.get_outer_class() classes.add(c.full_name_with_dots) for c in sorted(classes): sb(f'import {c};\n') def Generate(jni_mode, jni_obj, *, gen_jni_class, script_name): ctx = _Context(jni_obj, gen_jni_class, script_name, jni_mode.is_per_file) sb = common.StringBuilder() sb(f"""\ // // This file was generated by {script_name} // package {jni_obj.java_class.class_without_prefix.package_with_dots}; """) _imports(sb, ctx) sb('\n') visibility = 'public ' if jni_obj.proxy_visibility == 'public' else '' class_name = ctx.proxy_class.name if not ctx.is_per_file: sb('@CheckDiscard("crbug.com/993421")\n') sb(f'{visibility}class {class_name} implements {ctx.interface_name}') with sb.block(): _class_body(sb, ctx) return sb.to_string()