1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
|
# 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/internal/NullUnmarked'),
java_types.JavaClass('org/jni_zero/internal/Nullable'),
]
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.is_primitive() and not p.java_type.nullable:
sb(f'assert {p.name} != null : "Parameter \\"{p.name}\\" was null. Add @Nullable to it?";\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, ctx, native, name):
sig_params = native.proxy_params.to_java_declaration(ctx.type_resolver)
return_type = native.proxy_return_type.to_java()
sb(f'private static native {return_type} {name}({sig_params});\n')
def _class_body(sb, ctx):
sb(f"""\
private static @Nullable JniTestInstanceHolder sOverride;
public static {ctx.interface_name} get() {{
JniTestInstanceHolder holder = sOverride;
if (holder != null && holder.value != null) {{
return ({ctx.interface_name}) holder.value;
}}
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, ctx, 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('@NullUnmarked\n')
sb(f'{visibility}class {class_name} implements {ctx.interface_name}')
with sb.block():
_class_body(sb, ctx)
return sb.to_string()
|