xref: /aosp_15_r20/external/cronet/third_party/jni_zero/codegen/proxy_impl_java.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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