• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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