Reland "JNI Zero: Use two different outputs (.cc & .h) for generate_final_jni"

This reverts commit 1029bc2df841a5d3a6d2bee18b26d8c430ddf1b0.

Reason for revert: includes gn2bp fixes

NOTE: the gn2bp fixes were entirely vibe-coded for expediency to make
gn2bp green as quickly as possible. Android-side tests should provide
confidence the output is correct, even though the code may not be
optimal.

AI-generated description for the gn2bp fixes follows:

The upstream CL changed JNI registration generation to produce both
C++ source and header files. Previously, gn2bp translated this into
a single cc_genrule producing both files, which was then included in
the srcs of a cc_library_static.

Soong does not support header files in the srcs list of a library
and fails with: "srcs: Header file ... is not supported".

Fix this by splitting the JNI registration genrule into two:
1. A source genrule that only outputs the .cc file, included in srcs.
2. A headers genrule that only outputs the .h file, included in
   generated_headers.

The headers genrule exports both the root gen directory and the
subdirectory where the header is located to support different include
styles (local vs path-prefixed).

TAG=agy
CONV=101588e6-720b-4982-83a1-98f0c8be7a9d

Original change's description:
> Revert "JNI Zero: Use two different outputs (.cc & .h) for generate_final_jni"
>
> This reverts commit b3dd37dd8229c902b4c65d7b94d7b46ff63b2dc0.
>
> Reason for revert: breaks Cronet gn2bp - https://crbug.com/542041778. It does not appear to be a trivial fix - I have spent the day trying to fix gn2bp to no avail. I will keep working on it but in the meantime I would like the CL to be reverted so that gn2bp can go back to green for the duration of the work.
>
> Failure Link: https://ci.chromium.org/ui/p/chrome/builders/trusted.robots/cronet-arm64-gn2bp-debug/5393
>
> Original change's description:
> > JNI Zero: Use two different outputs (.cc & .h) for generate_final_jni
> >
> > It was confusing to have "--header-path=foo.cc".
> >
> > The .h file now has only a RegisterNatives() declaration, and the definition and multiplexing entry points live in the .cc.
> >
> > This also reworks generate_final_jni() to work the same was as
> > generate_jni() with respect to secondary toolchains. It now
> > defines the codegen action only in the default toolchain, but has
> > a source_set in the current toolchain. This simplifies webview's
> > usage of it.
> >
> > Change-Id: Ib9dda8645974f24f03c5d9351f6d674a56bfa582
> > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8160566
> > Reviewed-by: Martin Kong <martinkong@google.com>
> > Owners-Override: Andrew Grieve <agrieve@chromium.org>
> > Commit-Queue: Andrew Grieve <agrieve@chromium.org>
> > Cr-Commit-Position: refs/heads/main@{#1671852}
>
> Change-Id: Ib927a320ac7edc16aab3f6ab9a80047989f1b0bc
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8173647
> Auto-Submit: Etienne Dechamps <edechamps@google.com>
> Reviewed-by: Andrew Grieve <agrieve@chromium.org>
> Bot-Commit: rubber-stamper@appspot.gserviceaccount.com <rubber-stamper@appspot.gserviceaccount.com>
> Reviewed-by: Peter Beverloo <peter@chromium.org>
> Commit-Queue: Peter Beverloo <peter@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1673226}

Change-Id: Ibda003f3fcf8a6faae69f34f03ba9c4c5868b93e
Bug: b:542041778
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8193298
Owners-Override: Andrew Grieve <agrieve@chromium.org>
Commit-Queue: Andrew Grieve <agrieve@chromium.org>
Auto-Submit: Etienne Dechamps <edechamps@google.com>
Reviewed-by: Mohannad Farrag <aymanm@google.com>
Reviewed-by: Andrew Grieve <agrieve@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1674962}
NOKEYCHECK=True
GitOrigin-RevId: c3fa53c87882db9b370e1d828631c9aa67da79fd
diff --git a/codegen/header_common.py b/codegen/header_common.py
index d346efa..52479f3 100644
--- a/codegen/header_common.py
+++ b/codegen/header_common.py
@@ -51,8 +51,7 @@
                     user_includes=None,
                     header_guard=None,
                     is_shared_header=False):
-  if header_guard is None:
-    assert java_class is not None
+  if header_guard is None and java_class is not None:
     if is_shared_header:
       header_guard = f'{java_class.to_cpp()}_SHARED_JNI'
     else:
@@ -68,7 +67,8 @@
 //     {java_class.full_name_with_dots}
 
 """)
-  sb.append(f"""\
+  if header_guard:
+    sb.append(f"""\
 #ifndef {header_guard}
 #define {header_guard}
 
@@ -92,7 +92,8 @@
   sb = []
   if not is_shared_header:
     sb.append('#pragma clang diagnostic pop\n')
-  sb.append(f'#endif  // {header_guard}\n')
+  if header_guard:
+    sb.append(f'#endif  // {header_guard}\n')
   epilogue = ''.join(sb)
 
   return preamble, epilogue
diff --git a/codegen/register_natives.py b/codegen/register_natives.py
index dde1d8b..26c576d 100644
--- a/codegen/register_natives.py
+++ b/codegen/register_natives.py
@@ -80,7 +80,7 @@
                            gen_jni_class=None,
                            register_natives_name='RegisterNatives'):
   """RegisterNatives() that calls the helper RegisterNatives() methods."""
-  with sb.namespace(namespace or ''):
+  with sb.namespace(namespace):
     sb(f'bool {register_natives_name}(JNIEnv* env)')
     with sb.block():
       if gen_jni_class:
diff --git a/jni_registration_generator.py b/jni_registration_generator.py
index 88a8572..0f841f8 100644
--- a/jni_registration_generator.py
+++ b/jni_registration_generator.py
@@ -136,9 +136,6 @@
   present_jni_objs = list(
       _Flatten(jni_objs_by_path, native_sources_set & java_sources_set))
 
-  def RemoveTestOnlyNatives(self):
-    self.natives = [n for n in self.natives if not n.is_test_only]
-
   # Can contain path not in present_jni_objs.
   priority_sources_list = priority_java_sources.get(
       args.module_name, []) if priority_java_sources else []
@@ -191,19 +188,24 @@
       package_prefix=args.package_prefix,
       package_prefix_filter=args.package_prefix_filter)
 
-  if args.header_path:
-    if not args.include_test_only:
-      for jni_obj in present_jni_objs:
-        jni_obj.RemoveTestOnlyNatives()
+  if not args.include_test_only:
+    for jni_obj in present_jni_objs:
+      jni_obj.RemoveTestOnlyNatives()
 
-    if jni_mode.is_hashing or jni_mode.is_muxing:
-      gen_jni_class = short_gen_jni_class
-    else:
-      gen_jni_class = full_gen_jni_class
-    header_content = _CreateHeader(jni_mode, present_jni_objs,
-                                   boundary_proxy_natives, gen_jni_class, args,
-                                   muxed_aliases_by_sig, whole_hash,
-                                   priority_hash)
+  if jni_mode.is_hashing or jni_mode.is_muxing:
+    gen_jni_class = short_gen_jni_class
+  else:
+    gen_jni_class = full_gen_jni_class
+
+  if args.impl_path:
+    impl_content = _CreateImpl(jni_mode, present_jni_objs,
+                               boundary_proxy_natives, gen_jni_class, args,
+                               muxed_aliases_by_sig, whole_hash, priority_hash)
+    with common.atomic_output(args.impl_path, mode='w') as f:
+      f.write(impl_content)
+
+  if args.header_path:
+    header_content = _CreateHeader(args)
     with common.atomic_output(args.header_path, mode='w') as f:
       f.write(header_content)
 
@@ -316,13 +318,31 @@
   return whole_ret, priority_ret
 
 
-def _CreateHeader(jni_mode, jni_objs, boundary_proxy_natives, gen_jni_class,
-                  args, muxed_aliases_by_sig, whole_hash, priority_hash):
+def _CreateHeader(args):
   """Returns the content of the header file."""
   header_guard = os.path.splitext(args.header_path)[0].upper() + '_'
   header_guard = re.sub(r'[/.-]', '_', header_guard)
 
+  preamble, epilogue = header_common.header_preamble(
+      jni_generator.GetScriptName(),
+      system_includes=['jni.h'],
+      header_guard=header_guard,
+      is_shared_header=True)
+
+  sb = common.StringBuilder()
+  sb(preamble)
+  with sb.namespace(args.namespace):
+    sb(f'bool {args.register_natives_name}(JNIEnv* env);\n')
+  sb(epilogue)
+  return sb.to_string()
+
+
+def _CreateImpl(jni_mode, jni_objs, boundary_proxy_natives, gen_jni_class, args,
+                muxed_aliases_by_sig, whole_hash, priority_hash):
+  """Returns the content of the implementation file (.cc)."""
   user_includes = [f'{args.include_path_prefix}jni_zero_internal.h']
+  if args.header_path:
+    user_includes.append(os.path.basename(args.header_path))
   if args.extra_includes:
     user_includes += args.extra_includes
 
@@ -331,12 +351,13 @@
       java_class=gen_jni_class,
       system_includes=['iterator'],  # For std::size().
       user_includes=user_includes,
-      header_guard=header_guard)
+      header_guard=False,
+      is_shared_header=True)
 
   module_name = args.module_name or ''
 
   sb = common.StringBuilder()
-  sb.line(preamble)
+  sb(preamble)
   if jni_mode.is_muxing:
     sb(f"""\
 extern const int64_t kJniZeroHash{module_name}Whole = {whole_hash}LL;
@@ -348,16 +369,19 @@
   ]
   non_proxy_natives_java_classes.sort()
 
-  if non_proxy_natives_java_classes:
+  if args.manual_jni_registration and non_proxy_natives_java_classes:
     with sb.section('Class Accessors.'):
       header_common.class_accessors(sb, non_proxy_natives_java_classes)
 
-  with sb.section('Forward Declarations.'):
-    for jni_obj in jni_objs:
-      for native in jni_obj.natives:
-        with sb.statement():
-          natives_header.entry_point_declaration(sb, jni_mode, jni_obj, native,
-                                                 gen_jni_class)
+  if jni_mode.is_muxing or args.manual_jni_registration:
+    with sb.section('Forward Declarations.'):
+      for jni_obj in jni_objs:
+        natives = (jni_obj.natives
+                   if args.manual_jni_registration else jni_obj.proxy_natives)
+        for native in natives:
+          with sb.statement():
+            natives_header.entry_point_declaration(sb, jni_mode, jni_obj,
+                                                   native, gen_jni_class)
 
   if jni_mode.is_muxing and boundary_proxy_natives:
     with sb.section('Multiplexing Methods.'):
@@ -367,7 +391,7 @@
                                                     gen_jni_class)
 
   if args.manual_jni_registration:
-    # Helper methods use presence of gen_jni_class to denote presense of proxy
+    # Helper methods use presence of gen_jni_class to denote presence of proxy
     # methods.
     if not boundary_proxy_natives:
       gen_jni_class = None
@@ -428,10 +452,14 @@
 
 
 def main(parser, args, jni_mode):
-  if not args.header_path and args.manual_jni_registration:
+  if args.header_path and not args.manual_jni_registration:
+    parser.error('--header-path requires --manual-jni-registration.')
+  if args.manual_jni_registration and not args.header_path:
     parser.error('--manual-jni-registration requires --header-path.')
-  if not args.header_path and jni_mode.is_muxing:
-    parser.error('--enable-jni-multiplexing requires --header-path.')
+  if args.manual_jni_registration and not args.impl_path:
+    parser.error('--manual-jni-registration requires --impl-path.')
+  if jni_mode.is_muxing and not args.impl_path:
+    parser.error('--enable-jni-multiplexing requires --impl-path.')
   if args.remove_uncalled_methods and not args.native_sources_file:
     parser.error('--remove-uncalled-methods requires --native-sources-file.')
   if args.priority_java_sources_file:
diff --git a/jni_zero.gni b/jni_zero.gni
index 8c98d84..57e0fa7 100644
--- a/jni_zero.gni
+++ b/jni_zero.gni
@@ -126,125 +126,138 @@
 #     will always be placed first in the sequential switch numbers. (optional)
 #
 # Example
-#   generate_final_jni("chrome_jni_registration") {
+#   generate_final_jni("chrome_final_jni") {
 #     java_targets = [ ":chrome_public_apk" ]
 #     manual_jni_registration = false
 #   }
 template("generate_final_jni") {
-  forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
-  if (defined(invoker.native_deps)) {
-    _native_sources_list = "$target_gen_dir/$target_name.nativesources.txt"
-    jni_sources_list("${target_name}__native_sources") {
-      deps = invoker.native_deps
-      output = _native_sources_list
+  _target_name = target_name
+  _manual_jni_registration = defined(invoker.manual_jni_registration) &&
+                             invoker.manual_jni_registration
+  _needs_cpp_codegen = _manual_jni_registration || enable_jni_multiplexing
+  if (_needs_cpp_codegen) {
+    _subdir = rebase_path(target_gen_dir, root_gen_dir)
+    _cc_output = "$jni_headers_dir/$_subdir/$_target_name.cc"
+    if (_manual_jni_registration) {
+      _header_output = "$jni_headers_dir/$_subdir/$_target_name.h"
     }
   }
 
-  _java_sources_list = "$target_gen_dir/$target_name.javasources.txt"
-  jni_sources_list("${target_name}__java_sources") {
-    deps = invoker.java_targets
-    output = _java_sources_list
+  if (current_toolchain == default_toolchain) {
+    if (defined(invoker.native_deps)) {
+      _native_sources_list = "$target_gen_dir/$_target_name.nativesources.txt"
+      jni_sources_list("${_target_name}__native_sources") {
+        forward_variables_from(invoker, [ "testonly" ])
+        deps = invoker.native_deps
+        output = _native_sources_list
+      }
+    }
 
-    # When apk or bundle module targets are uses, do not pull metadata from
-    # their native library deps.
-    walk_keys = [ "java_walk_keys" ]
-  }
-
-  if (enable_jni_multiplexing && defined(invoker.priority_java_targets)) {
-    # This is intended for WebView, so we can only look at Java sources. This
-    # is becuase WebView's java is a strict subset of it's native, so we don't
-    # need to take the intersection of it's native and java.
-    _priority_java_sources_list =
-        "$target_gen_dir/$target_name.priority_javasources.txt"
-    jni_sources_list("${target_name}__priority_java_sources") {
-      deps = invoker.priority_java_targets
-      output = _priority_java_sources_list
+    _java_sources_list = "$target_gen_dir/$_target_name.javasources.txt"
+    jni_sources_list("${_target_name}__java_sources") {
+      forward_variables_from(invoker, [ "testonly" ])
+      deps = invoker.java_targets
+      output = _java_sources_list
 
       # When apk or bundle module targets are uses, do not pull metadata from
       # their native library deps.
       walk_keys = [ "java_walk_keys" ]
     }
-  }
 
-  _invoke_jni_zero(target_name) {
-    gen_deps = [ ":${target_name}__java_sources" ]
-    if (defined(_native_sources_list)) {
-      gen_deps += [ ":${target_name}__native_sources" ]
-    }
-    if (defined(_priority_java_sources_list)) {
-      gen_deps += [ ":${target_name}__priority_java_sources" ]
-    }
+    if (enable_jni_multiplexing && defined(invoker.priority_java_targets)) {
+      # This is intended for WebView, so we can only look at Java sources. This
+      # is becuase WebView's java is a strict subset of it's native, so we don't
+      # need to take the intersection of it's native and java.
+      _priority_java_sources_list =
+          "$target_gen_dir/$_target_name.priority_javasources.txt"
+      jni_sources_list("${_target_name}__priority_java_sources") {
+        forward_variables_from(invoker, [ "testonly" ])
+        deps = invoker.priority_java_targets
+        output = _priority_java_sources_list
 
-    # Cannot depend on jni_sources_list targets since they likely depend on
-    # this target via srcjar_deps. Depfiles are used to add the dep instead.
-    deps = []
-    _srcjar_output = "$target_gen_dir/$target_name.srcjar"
-    outputs = [ _srcjar_output ]
-    depfile = "$target_gen_dir/$target_name.d"
-
-    java_target_deps = []
-    if (defined(invoker.java_targets)) {
-      foreach(java_targets_dep, invoker.java_targets) {
-        java_target_deps +=
-            [ get_label_info(java_targets_dep, "label_no_toolchain") ]
+        # When apk or bundle module targets are uses, do not pull metadata from
+        # their native library deps.
+        walk_keys = [ "java_walk_keys" ]
       }
     }
 
-    args = [
-      "generate-final",
-      "--srcjar-path",
-      rebase_path(_srcjar_output, root_build_dir),
-      "--depfile",
-      rebase_path(depfile, root_build_dir),
-      "--java-sources-file",
-      rebase_path(_java_sources_list, root_build_dir),
-    ]
-
-    if (defined(_native_sources_list)) {
-      args += [
-        "--native-sources-file",
-        rebase_path(_native_sources_list, root_build_dir),
-      ]
-    }
-
-    if (defined(invoker.include_testonly)) {
-      _include_testonly = invoker.include_testonly
-    } else {
-      _include_testonly = defined(testonly) && testonly
-    }
-    if (_include_testonly) {
-      args += [ "--include-test-only" ]
-    }
-
-    if (use_hashed_jni_names) {
-      args += [ "--use-proxy-hash" ]
-    }
-
-    if (defined(invoker.remove_uncalled_jni) && invoker.remove_uncalled_jni) {
-      args += [ "--remove-uncalled-methods" ]
-    }
-    if (defined(invoker.add_stubs_for_missing_jni) &&
-        invoker.add_stubs_for_missing_jni) {
-      args += [ "--add-stubs-for-missing-native" ]
-    }
-
-    if (defined(invoker.cpp_codegen_output)) {
-      _cpp_codegen_output = invoker.cpp_codegen_output
-      outputs += [ _cpp_codegen_output ]
-      args += [
-        "--header-path",
-        rebase_path(_cpp_codegen_output, root_build_dir),
-      ]
-
-      public_configs = [
-        # This gives targets depending on this registration access to our
-        # generated C++ file.
-        "//third_party/jni_zero:jni_include_dir",
-      ]
-      if (defined(invoker.manual_jni_registration) &&
-          invoker.manual_jni_registration) {
-        args += [ "--manual-jni-registration" ]
+    _invoke_jni_zero(_target_name) {
+      forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
+      if (defined(visibility) && _needs_cpp_codegen) {
+        visibility += [ ":${_target_name}_source_set" ]
       }
+      gen_deps = [ ":${_target_name}__java_sources" ]
+      if (defined(_native_sources_list)) {
+        gen_deps += [ ":${_target_name}__native_sources" ]
+      }
+      if (defined(_priority_java_sources_list)) {
+        gen_deps += [ ":${_target_name}__priority_java_sources" ]
+      }
+
+      # Cannot depend on jni_sources_list targets since they likely depend on
+      # this target via srcjar_deps. Depfiles are used to add the dep instead.
+      deps = []
+      _srcjar_output = "$target_gen_dir/$_target_name.srcjar"
+      outputs = [ _srcjar_output ]
+      if (_needs_cpp_codegen) {
+        outputs += [ _cc_output ]
+      }
+      depfile = "$target_gen_dir/$_target_name.d"
+
+      args = [
+        "generate-final",
+        "--srcjar-path",
+        rebase_path(_srcjar_output, root_build_dir),
+        "--depfile",
+        rebase_path(depfile, root_build_dir),
+        "--java-sources-file",
+        rebase_path(_java_sources_list, root_build_dir),
+      ]
+
+      if (_needs_cpp_codegen) {
+        args += [
+          "--impl-path",
+          rebase_path(_cc_output, root_build_dir),
+        ]
+      }
+
+      if (defined(_native_sources_list)) {
+        args += [
+          "--native-sources-file",
+          rebase_path(_native_sources_list, root_build_dir),
+        ]
+      }
+
+      if (defined(invoker.include_testonly)) {
+        _include_testonly = invoker.include_testonly
+      } else {
+        _include_testonly = defined(testonly) && testonly
+      }
+      if (_include_testonly) {
+        args += [ "--include-test-only" ]
+      }
+
+      if (use_hashed_jni_names) {
+        args += [ "--use-proxy-hash" ]
+      }
+
+      if (defined(invoker.remove_uncalled_jni) && invoker.remove_uncalled_jni) {
+        args += [ "--remove-uncalled-methods" ]
+      }
+      if (defined(invoker.add_stubs_for_missing_jni) &&
+          invoker.add_stubs_for_missing_jni) {
+        args += [ "--add-stubs-for-missing-native" ]
+      }
+
+      if (defined(_header_output)) {
+        outputs += [ _header_output ]
+        args += [
+          "--header-path",
+          rebase_path(_header_output, root_build_dir),
+          "--manual-jni-registration",
+        ]
+      }
+
       if (enable_jni_multiplexing) {
         args += [ "--enable-jni-multiplexing" ]
 
@@ -258,26 +271,47 @@
             invoker.never_omit_switch_num) {
           args += [ "--never-omit-switch-num" ]
         }
+      } else {
+        not_needed(invoker,
+                   [
+                     "never_omit_switch_num",
+                     "priority_java_targets",
+                   ])
       }
-    } else {
-      not_needed(invoker,
-                 [
-                   "never_omit_switch_num",
-                   "priority_java_targets",
-                 ])
-    }
 
-    if (defined(invoker.namespace)) {
-      args += [ "--namespace=${invoker.namespace}" ]
-    }
+      if (defined(invoker.namespace)) {
+        args += [ "--namespace=${invoker.namespace}" ]
+      }
 
-    if (defined(invoker.module_name)) {
-      args += [ "--module-name=${invoker.module_name}" ]
-    }
+      if (defined(invoker.module_name)) {
+        args += [ "--module-name=${invoker.module_name}" ]
+      }
 
-    # Cronet needs to rename jni_zero classes
-    if (defined(is_cronet_build) && is_cronet_build) {
-      args += _cronet_renaming_extra_args
+      # Cronet needs to rename jni_zero classes
+      if (defined(is_cronet_build) && is_cronet_build) {
+        args += _cronet_renaming_extra_args
+      }
+    }
+  } else {  # current_toolchain != default_toolchain
+    not_needed(invoker, "*")
+    if (!_needs_cpp_codegen) {
+      not_needed([ "_target_name" ])
+    }
+  }
+
+  if (_needs_cpp_codegen) {
+    source_set("${_target_name}_source_set") {
+      forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
+      sources = [ _cc_output ]
+      if (defined(_header_output)) {
+        sources += [ _header_output ]
+      }
+      deps = [ ":$_target_name($default_toolchain)" ]
+      public_configs =
+          [ "//third_party/jni_zero:jni_include_dir($default_toolchain)" ]
+      if (is_robolectric) {
+        configs += [ "//third_party/jdk" ]
+      }
     }
   }
 }
@@ -394,21 +428,21 @@
       }
     }
   } else {
-    _final_target_name = target_name
+    _target_name = target_name
     if (defined(invoker.classes)) {
       _from_source = false
     } else {
       _from_source = true
 
       # Using final_target_name to make srcjar_deps work.
-      _srcjar_output = "$target_gen_dir/$_final_target_name.srcjar"
+      _srcjar_output = "$target_gen_dir/$_target_name.srcjar"
       _placeholder_srcjar_output =
-          "$target_gen_dir/${_final_target_name}_placeholder.srcjar"
+          "$target_gen_dir/${_target_name}_placeholder.srcjar"
     }
 
     _invoke_jni_zero(_jni_zero_action_target_name) {
       _subdir = rebase_path(target_gen_dir, root_gen_dir)
-      _jni_output_dir = "$jni_headers_dir/$_subdir/$_final_target_name"
+      _jni_output_dir = "$jni_headers_dir/$_subdir/$_target_name"
 
       # The sources aren't compiled so don't check their dependencies.
       check_includes = false
@@ -438,7 +472,7 @@
       if (defined(invoker.enable_resolved_types_validation) &&
           invoker.enable_resolved_types_validation) {
         _resolved_types_path =
-            "$target_gen_dir/$_final_target_name.resolved_types.txt"
+            "$target_gen_dir/$_target_name.resolved_types.txt"
         outputs += [ _resolved_types_path ]
         args += [
           "--resolved-types-path",
@@ -542,7 +576,7 @@
     }
 
     if (_from_source) {
-      java_library("${_final_target_name}_java") {
+      java_library("${_target_name}_java") {
         forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
 
         srcjars = [
@@ -567,7 +601,7 @@
         }
       }
     } else {
-      java_group("${_final_target_name}_java") {
+      java_group("${_target_name}_java") {
         # Allows target to appear as a public_dep of another generate_jni().
       }
     }
@@ -575,7 +609,7 @@
     # This group exists to allow for users of generate_jni() to get our object
     # files included in their executables without explicitly depending on our
     # targets in jni_zero/BUILD.gn.
-    group(_final_target_name) {
+    group(_target_name) {
       public_deps = [ ":$_jni_zero_action_target_name" ]
       forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
       if (defined(visibility)) {
@@ -676,82 +710,45 @@
 template("native_with_jni") {
   _enable_underlying_native =
       !defined(invoker.enable_target) || invoker.enable_target
-  _manual_jni_registration = defined(invoker.manual_jni_registration) &&
-                             invoker.manual_jni_registration
-  _needs_cpp_codegen =
-      (_manual_jni_registration || enable_jni_multiplexing) &&
-      !(defined(invoker.collect_inputs_only) && invoker.collect_inputs_only)
-  _needs_native_dep = _enable_underlying_native && _needs_cpp_codegen
-  _force_current_toolchain = defined(invoker.force_current_toolchain) &&
-                             invoker.force_current_toolchain
-  _use_current_toolchain =
-      _force_current_toolchain || current_toolchain == default_toolchain
-  if ((_needs_cpp_codegen && _use_current_toolchain) || _needs_native_dep) {
-    _subdir = rebase_path(target_gen_dir, root_gen_dir)
-    _registration_cpp_codegen_output =
-        "$jni_headers_dir/$_subdir/${target_name}__jni_registration_generated"
-
-    # Make it a header for jni_registration (where we need to #include it) and
-    # .cc when it's multiplexing.
-    if (_manual_jni_registration) {
-      _registration_cpp_codegen_output += ".h"
-    } else {
-      _registration_cpp_codegen_output += ".cc"
-    }
+  if (defined(invoker.visibility)) {
+    _target_name_for_visibility = target_name
   }
-  if (_needs_native_dep || _use_current_toolchain) {
-    _jni_registration_target_name = "${target_name}__jni_registration"
-  }
-
-  if (_use_current_toolchain) {
-    if (defined(invoker.visibility)) {
-      _target_name_for_visibility = target_name
+  _final_jni_target_name = "${target_name}__jni_registration"
+  generate_final_jni(_final_jni_target_name) {
+    forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
+    if (defined(visibility)) {
+      visibility += [ ":$_target_name_for_visibility" ]
     }
-    generate_final_jni(_jni_registration_target_name) {
-      forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
-      if (defined(visibility)) {
-        visibility += [ ":$_target_name_for_visibility" ]
-      }
-      native_deps = invoker.deps
+    native_deps = invoker.deps
 
-      if (defined(invoker.testonly) && invoker.testonly) {
-        if (!defined(invoker.add_stubs_for_missing_jni)) {
-          add_stubs_for_missing_jni = true
-        }
-        if (!defined(invoker.remove_uncalled_jni)) {
-          remove_uncalled_jni = true
-        }
+    if (defined(invoker.testonly) && invoker.testonly) {
+      if (!defined(invoker.add_stubs_for_missing_jni)) {
+        add_stubs_for_missing_jni = true
       }
-      if (_needs_cpp_codegen) {
-        cpp_codegen_output = _registration_cpp_codegen_output
+      if (!defined(invoker.remove_uncalled_jni)) {
+        remove_uncalled_jni = true
       }
-      forward_variables_from(invoker,
-                             [
-                               "add_stubs_for_missing_jni",
-                               "java_targets",
-                               "manual_jni_registration",
-                               "module_name",
-                               "namespace",
-                               "never_omit_switch_num",
-                               "remove_uncalled_jni",
-                               "priority_java_targets",
-                             ])
     }
-  } else {
-    not_needed(invoker,
-               [
-                 "add_stubs_for_missing_jni",
-                 "java_targets",
-                 "manual_jni_registration",
-                 "module_name",
-                 "namespace",
-                 "never_omit_switch_num",
-                 "remove_uncalled_jni",
-                 "priority_java_targets",
-               ])
+    forward_variables_from(invoker,
+                           [
+                             "add_stubs_for_missing_jni",
+                             "java_targets",
+                             "manual_jni_registration",
+                             "module_name",
+                             "namespace",
+                             "never_omit_switch_num",
+                             "remove_uncalled_jni",
+                             "priority_java_targets",
+                           ])
   }
 
   if (_enable_underlying_native) {
+    _manual_jni_registration = defined(invoker.manual_jni_registration) &&
+                               invoker.manual_jni_registration
+    _needs_cpp_codegen =
+        (_manual_jni_registration || enable_jni_multiplexing) &&
+        !(defined(invoker.collect_inputs_only) && invoker.collect_inputs_only)
+
     if (defined(invoker.target_type_import)) {
       import(invoker.target_type_import)
     }
@@ -765,7 +762,7 @@
       # already set the correct defaults in the invoker.
       configs = []
       configs = invoker.configs
-      if (_needs_native_dep) {
+      if (_needs_cpp_codegen) {
         configs +=
             [ "//third_party/jni_zero:jni_include_dir($default_toolchain)" ]
         if (is_robolectric &&
@@ -774,15 +771,7 @@
           # filter_include is for.
           configs += [ "//third_party/jdk" ]
         }
-        if (defined(invoker.jni_registration_toolchain)) {
-          deps += [ ":$_jni_registration_target_name(${invoker.jni_registration_toolchain})" ]
-        } else {
-          deps += [ ":$_jni_registration_target_name($default_toolchain)" ]
-        }
-        if (!defined(sources)) {
-          sources = []
-        }
-        sources += [ _registration_cpp_codegen_output ]
+        deps += [ ":${_final_jni_target_name}_source_set" ]
       }
       forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
       forward_variables_from(invoker,
diff --git a/jni_zero.py b/jni_zero.py
index c9c3826..723ce9c 100755
--- a/jni_zero.py
+++ b/jni_zero.py
@@ -96,6 +96,8 @@
     outputs.add_argument('--jni-pickle',
                          help='Path to write intermediate .jni.pickle file.')
   if is_final:
+    outputs.add_argument('--impl-path',
+                         help='Path to output C++ implementation file.')
     outputs.add_argument(
         '--depfile', help='Path to depfile (for use with ninja build system)')
 
diff --git a/test/golden/testEndToEndManualRegistration-Registration.h.golden b/test/golden/testEndToEndManualRegistration-Final.cc.golden
similarity index 96%
rename from test/golden/testEndToEndManualRegistration-Registration.h.golden
rename to test/golden/testEndToEndManualRegistration-Final.cc.golden
index 90f1a0b..7e58394 100644
--- a/test/golden/testEndToEndManualRegistration-Registration.h.golden
+++ b/test/golden/testEndToEndManualRegistration-Final.cc.golden
@@ -3,17 +3,10 @@
 // For
 //     org.jni_zero.GEN_JNI
 
-#ifndef TEMP_DIR_HEADER_
-#define TEMP_DIR_HEADER_
-
 #include <iterator>
 
 #include "overridden/jni_zero_internal.h"
-
-// Field, class, and method accessors use inline functions with static
-// variables for caching.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunique-object-duplication"
+#include "header.h"
 
 // Forward Declarations.
 JNI_ZERO_BOUNDARY_EXPORT jobject Java_org_jni_1zero_GEN_1JNI_org_1jni_11zero_1SampleForAnnotationProcessor_1bar(
@@ -209,8 +202,6 @@
 }  // namespace
 
 // Main Register Function.
-namespace {
-
 bool RegisterNatives(JNIEnv* env) {
   // Register natives in a proxy.
   if (!RegisterNative_org_jni_1zero_GEN_1JNI(env)) {
@@ -220,7 +211,3 @@
   return true;
 }
 
-}  // namespace
-
-#pragma clang diagnostic pop
-#endif  // TEMP_DIR_HEADER_
diff --git a/test/golden/testEndToEndManualRegistration-Final.h.golden b/test/golden/testEndToEndManualRegistration-Final.h.golden
new file mode 100644
index 0000000..4c39fe8
--- /dev/null
+++ b/test/golden/testEndToEndManualRegistration-Final.h.golden
@@ -0,0 +1,9 @@
+// This file was generated by
+//     //third_party/jni_zero/jni_zero.py
+#ifndef TEMP_DIR_HEADER_
+#define TEMP_DIR_HEADER_
+
+#include <jni.h>
+
+bool RegisterNatives(JNIEnv* env);
+#endif  // TEMP_DIR_HEADER_
diff --git a/test/golden/testEndToEndProxyHashed-Registration.h.golden b/test/golden/testEndToEndProxyHashed-Registration.h.golden
deleted file mode 100644
index ba456f9..0000000
--- a/test/golden/testEndToEndProxyHashed-Registration.h.golden
+++ /dev/null
@@ -1,91 +0,0 @@
-// This file was generated by
-//     //third_party/jni_zero/jni_zero.py
-// For
-//     J.N
-
-#ifndef TEMP_DIR_HEADER_
-#define TEMP_DIR_HEADER_
-
-#include <iterator>
-
-#include "overridden/jni_zero_internal.h"
-
-// Field, class, and method accessors use inline functions with static
-// variables for caching.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunique-object-duplication"
-
-// Forward Declarations.
-JNI_ZERO_BOUNDARY_EXPORT jobject Java_J_N_M_00024zHpGqF(
-    JNIEnv* env,
-    jclass jcaller,
-    jobject sample);
-JNI_ZERO_BOUNDARY_EXPORT void Java_J_N_MFxD5gzE(JNIEnv* env, jclass jcaller);
-JNI_ZERO_BOUNDARY_EXPORT bool Java_J_N_Mts1shVs(JNIEnv* env, jclass jcaller);
-JNI_ZERO_BOUNDARY_EXPORT jobject Java_J_N_MHC2XaYW(JNIEnv* env, jclass jcaller);
-JNI_ZERO_BOUNDARY_EXPORT jobject Java_J_N_MPl4TY3m(JNIEnv* env, jclass jcaller);
-JNI_ZERO_BOUNDARY_EXPORT jobject Java_J_N_MbcaLfAT(
-    JNIEnv* env,
-    jclass jcaller,
-    jobject __arrayClazz);
-JNI_ZERO_BOUNDARY_EXPORT jobject Java_J_N_MrxydxEH(JNIEnv* env, jclass jcaller);
-JNI_ZERO_BOUNDARY_EXPORT jobject Java_J_N_MaQxW6t6(JNIEnv* env, jclass jcaller);
-JNI_ZERO_BOUNDARY_EXPORT jobject Java_J_N_M8wEusw1(JNIEnv* env, jclass jcaller);
-JNI_ZERO_BOUNDARY_EXPORT jobject Java_J_N_MpVxNDSC(JNIEnv* env, jclass jcaller);
-JNI_ZERO_BOUNDARY_EXPORT jobject Java_J_N_M7jE3q3_00024(JNIEnv* env, jclass jcaller);
-JNI_ZERO_BOUNDARY_EXPORT jobject Java_J_N_MHY_1jLL4(JNIEnv* env, jclass jcaller);
-JNI_ZERO_BOUNDARY_EXPORT jobject Java_J_N_MF8oFFVb(JNIEnv* env, jclass jcaller);
-JNI_ZERO_BOUNDARY_EXPORT jobject Java_J_N_Mq7V3oMs(JNIEnv* env, jclass jcaller);
-JNI_ZERO_BOUNDARY_EXPORT jobject Java_J_N_MRdQpdMz(JNIEnv* env, jclass jcaller);
-JNI_ZERO_BOUNDARY_EXPORT jobject Java_J_N_M6SdcGCv(JNIEnv* env, jclass jcaller);
-JNI_ZERO_BOUNDARY_EXPORT jobject Java_J_N_Mnt3ot24(JNIEnv* env, jclass jcaller);
-JNI_ZERO_BOUNDARY_EXPORT jobject Java_J_N_Mepwik1_1(
-    JNIEnv* env,
-    jclass jcaller,
-    jobject stringToReverse);
-JNI_ZERO_BOUNDARY_EXPORT jobject Java_J_N_M5Naw4MO(JNIEnv* env, jclass jcaller, jobject strs);
-JNI_ZERO_BOUNDARY_EXPORT jobject Java_J_N_M3btId9A(JNIEnv* env, jclass jcaller, jobject strs);
-JNI_ZERO_BOUNDARY_EXPORT jobject Java_J_N_M3dkhKZM(
-    JNIEnv* env,
-    jclass jcaller,
-    int32_t zint,
-    jobject ints,
-    int64_t zlong,
-    jobject longs,
-    int16_t zshort,
-    jobject shorts,
-    uint16_t zchar,
-    jobject chars,
-    int8_t zbyte,
-    jobject bytes,
-    double zdouble,
-    jobject doubles,
-    float zfloat,
-    jobject floats,
-    bool zbool,
-    jobject bools);
-JNI_ZERO_BOUNDARY_EXPORT void Java_J_N_MomokuWo(
-    JNIEnv* env,
-    jclass jcaller,
-    jobject clazz,
-    jobject classes,
-    jobject throwable,
-    jobject throwables,
-    jobject string,
-    jobject strings,
-    jobject convertedString,
-    jobject convertedStrings,
-    jobject optionalString,
-    jobject funcType,
-    jobject tStruct,
-    jobject structs,
-    jobject obj,
-    jobject convertedObj,
-    jobject objects,
-    jobject nestedInterface,
-    jobject view,
-    jobject context,
-    jobject convertedObjects);
-
-#pragma clang diagnostic pop
-#endif  // TEMP_DIR_HEADER_
diff --git a/test/golden/testEndToEndProxyJniWithModules-Registration.h.golden b/test/golden/testEndToEndProxyJniWithModules-Final.cc.golden
similarity index 78%
rename from test/golden/testEndToEndProxyJniWithModules-Registration.h.golden
rename to test/golden/testEndToEndProxyJniWithModules-Final.cc.golden
index fc64e5d..71ebcff 100644
--- a/test/golden/testEndToEndProxyJniWithModules-Registration.h.golden
+++ b/test/golden/testEndToEndProxyJniWithModules-Final.cc.golden
@@ -3,17 +3,10 @@
 // For
 //     J.module_N
 
-#ifndef TEMP_DIR_HEADER_
-#define TEMP_DIR_HEADER_
-
 #include <iterator>
 
 #include "overridden/jni_zero_internal.h"
-
-// Field, class, and method accessors use inline functions with static
-// variables for caching.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunique-object-duplication"
+#include "header.h"
 
 // Forward Declarations.
 JNI_ZERO_BOUNDARY_EXPORT bool Java_J_module_1N_MejItBT4(JNIEnv* env, jclass jcaller, bool a);
@@ -45,8 +38,6 @@
 }  // namespace
 
 // Main Register Function.
-namespace {
-
 bool RegisterNatives(JNIEnv* env) {
   // Register natives in a proxy.
   if (!RegisterNative_J_module_1N(env)) {
@@ -56,7 +47,3 @@
   return true;
 }
 
-}  // namespace
-
-#pragma clang diagnostic pop
-#endif  // TEMP_DIR_HEADER_
diff --git a/test/golden/testEndToEndProxyJniWithModules-Final.h.golden b/test/golden/testEndToEndProxyJniWithModules-Final.h.golden
new file mode 100644
index 0000000..4c39fe8
--- /dev/null
+++ b/test/golden/testEndToEndProxyJniWithModules-Final.h.golden
@@ -0,0 +1,9 @@
+// This file was generated by
+//     //third_party/jni_zero/jni_zero.py
+#ifndef TEMP_DIR_HEADER_
+#define TEMP_DIR_HEADER_
+
+#include <jni.h>
+
+bool RegisterNatives(JNIEnv* env);
+#endif  // TEMP_DIR_HEADER_
diff --git a/test/golden/testForTestingKeptHash-Registration.h.golden b/test/golden/testForTestingKeptHash-Registration.h.golden
deleted file mode 100644
index 3bc98a6..0000000
--- a/test/golden/testForTestingKeptHash-Registration.h.golden
+++ /dev/null
@@ -1,44 +0,0 @@
-// This file was generated by
-//     //third_party/jni_zero/jni_zero.py
-// For
-//     J.N
-
-#ifndef TEMP_DIR_HEADER_
-#define TEMP_DIR_HEADER_
-
-#include <iterator>
-
-#include "overridden/jni_zero_internal.h"
-
-// Field, class, and method accessors use inline functions with static
-// variables for caching.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunique-object-duplication"
-
-// Forward Declarations.
-JNI_ZERO_BOUNDARY_EXPORT int32_t Java_J_N_MYplquli(
-    JNIEnv* env,
-    jclass jcaller,
-    jobject caller,
-    jobject b);
-JNI_ZERO_BOUNDARY_EXPORT jobject Java_J_N_M3RUFTzC(
-    JNIEnv* env,
-    jclass jcaller,
-    jobject a,
-    jobject b);
-JNI_ZERO_BOUNDARY_EXPORT jobject Java_J_N_MV9JEAYF(
-    JNIEnv* env,
-    jclass jcaller,
-    jobject a,
-    jobject b);
-JNI_ZERO_BOUNDARY_EXPORT void Java_J_N_MKWEYRlq(JNIEnv* env, jclass jcaller);
-JNI_ZERO_BOUNDARY_EXPORT jobject Java_J_N_MalqUoB_1(JNIEnv* env, jclass jcaller, jobject arg);
-JNI_ZERO_BOUNDARY_EXPORT void Java_J_N_M0E4cldm(JNIEnv* env, jclass jcaller, jobject b, jobject i);
-JNI_ZERO_BOUNDARY_EXPORT bool Java_J_N_MWbFhvyA(JNIEnv* env, jclass jcaller, jobject sb);
-JNI_ZERO_BOUNDARY_EXPORT int32_t Java_J_N_MWbFhvyA(JNIEnv* env, jclass jcaller, int32_t sb);
-JNI_ZERO_BOUNDARY_EXPORT void Java_J_N_M3noEmrf_1ForTesting(JNIEnv* env, jclass jcaller);
-JNI_ZERO_BOUNDARY_EXPORT void Java_J_N_M1XMhKEd_1ForTesting(JNIEnv* env, jclass jcaller);
-JNI_ZERO_BOUNDARY_EXPORT void Java_J_N_M0ykt7m_1_1ForTesting(JNIEnv* env, jclass jcaller);
-
-#pragma clang diagnostic pop
-#endif  // TEMP_DIR_HEADER_
diff --git a/test/golden/testForTestingKeptMultiplexing-Registration.h.golden b/test/golden/testForTestingKeptMultiplexing-Final.cc.golden
similarity index 91%
rename from test/golden/testForTestingKeptMultiplexing-Registration.h.golden
rename to test/golden/testForTestingKeptMultiplexing-Final.cc.golden
index 1ced388..c0224a7 100644
--- a/test/golden/testForTestingKeptMultiplexing-Registration.h.golden
+++ b/test/golden/testForTestingKeptMultiplexing-Final.cc.golden
@@ -3,18 +3,10 @@
 // For
 //     J.N
 
-#ifndef TEMP_DIR_HEADER_
-#define TEMP_DIR_HEADER_
-
 #include <iterator>
 
 #include "overridden/jni_zero_internal.h"
 
-// Field, class, and method accessors use inline functions with static
-// variables for caching.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunique-object-duplication"
-
 extern const int64_t kJniZeroHashWhole = 2249885413155022725LL;
 extern const int64_t kJniZeroHashPriority = 0LL;
 
@@ -98,5 +90,3 @@
   }
 }
 
-#pragma clang diagnostic pop
-#endif  // TEMP_DIR_HEADER_
diff --git a/test/golden/testForTestingRemovedHash-Registration.h.golden b/test/golden/testForTestingRemovedHash-Registration.h.golden
deleted file mode 100644
index 9f01263..0000000
--- a/test/golden/testForTestingRemovedHash-Registration.h.golden
+++ /dev/null
@@ -1,41 +0,0 @@
-// This file was generated by
-//     //third_party/jni_zero/jni_zero.py
-// For
-//     J.N
-
-#ifndef TEMP_DIR_HEADER_
-#define TEMP_DIR_HEADER_
-
-#include <iterator>
-
-#include "overridden/jni_zero_internal.h"
-
-// Field, class, and method accessors use inline functions with static
-// variables for caching.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunique-object-duplication"
-
-// Forward Declarations.
-JNI_ZERO_BOUNDARY_EXPORT int32_t Java_J_N_MYplquli(
-    JNIEnv* env,
-    jclass jcaller,
-    jobject caller,
-    jobject b);
-JNI_ZERO_BOUNDARY_EXPORT jobject Java_J_N_M3RUFTzC(
-    JNIEnv* env,
-    jclass jcaller,
-    jobject a,
-    jobject b);
-JNI_ZERO_BOUNDARY_EXPORT jobject Java_J_N_MV9JEAYF(
-    JNIEnv* env,
-    jclass jcaller,
-    jobject a,
-    jobject b);
-JNI_ZERO_BOUNDARY_EXPORT void Java_J_N_MKWEYRlq(JNIEnv* env, jclass jcaller);
-JNI_ZERO_BOUNDARY_EXPORT jobject Java_J_N_MalqUoB_1(JNIEnv* env, jclass jcaller, jobject arg);
-JNI_ZERO_BOUNDARY_EXPORT void Java_J_N_M0E4cldm(JNIEnv* env, jclass jcaller, jobject b, jobject i);
-JNI_ZERO_BOUNDARY_EXPORT bool Java_J_N_MWbFhvyA(JNIEnv* env, jclass jcaller, jobject sb);
-JNI_ZERO_BOUNDARY_EXPORT int32_t Java_J_N_MWbFhvyA(JNIEnv* env, jclass jcaller, int32_t sb);
-
-#pragma clang diagnostic pop
-#endif  // TEMP_DIR_HEADER_
diff --git a/test/golden/testForTestingRemovedMultiplexing-Registration.h.golden b/test/golden/testForTestingRemovedMultiplexing-Final.cc.golden
similarity index 90%
rename from test/golden/testForTestingRemovedMultiplexing-Registration.h.golden
rename to test/golden/testForTestingRemovedMultiplexing-Final.cc.golden
index 511f8cf..89b463f 100644
--- a/test/golden/testForTestingRemovedMultiplexing-Registration.h.golden
+++ b/test/golden/testForTestingRemovedMultiplexing-Final.cc.golden
@@ -3,18 +3,10 @@
 // For
 //     J.N
 
-#ifndef TEMP_DIR_HEADER_
-#define TEMP_DIR_HEADER_
-
 #include <iterator>
 
 #include "overridden/jni_zero_internal.h"
 
-// Field, class, and method accessors use inline functions with static
-// variables for caching.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunique-object-duplication"
-
 extern const int64_t kJniZeroHashWhole = 2249885413155022725LL;
 extern const int64_t kJniZeroHashPriority = 0LL;
 
@@ -83,5 +75,3 @@
   }
 }
 
-#pragma clang diagnostic pop
-#endif  // TEMP_DIR_HEADER_
diff --git a/test/golden/testGenRegisterNatives-Registration.h.golden b/test/golden/testGenRegisterNatives-Final.h.golden
similarity index 98%
rename from test/golden/testGenRegisterNatives-Registration.h.golden
rename to test/golden/testGenRegisterNatives-Final.h.golden
index 6b3e93e..b6f2b03 100644
--- a/test/golden/testGenRegisterNatives-Registration.h.golden
+++ b/test/golden/testGenRegisterNatives-Final.h.golden
@@ -65,8 +65,6 @@
 }  // namespace
 
 // Main Register Function.
-namespace {
-
 bool RegisterNativesForTest(JNIEnv* env) {
   if (!RegisterNative_org_jni_1zero_SampleForLinker(env))
     return false;
@@ -74,7 +72,5 @@
   return true;
 }
 
-}  // namespace
-
 #pragma clang diagnostic pop
 #endif  // TEMP_DIR_HEADER_
diff --git a/test/golden/testModulesWithMultiplexing-Registration.h.golden b/test/golden/testModulesWithMultiplexing-Final.cc.golden
similarity index 82%
rename from test/golden/testModulesWithMultiplexing-Registration.h.golden
rename to test/golden/testModulesWithMultiplexing-Final.cc.golden
index 728a8b0..48b6f90 100644
--- a/test/golden/testModulesWithMultiplexing-Registration.h.golden
+++ b/test/golden/testModulesWithMultiplexing-Final.cc.golden
@@ -3,17 +3,10 @@
 // For
 //     J.module_N
 
-#ifndef TEMP_DIR_HEADER_
-#define TEMP_DIR_HEADER_
-
 #include <iterator>
 
 #include "overridden/jni_zero_internal.h"
-
-// Field, class, and method accessors use inline functions with static
-// variables for caching.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunique-object-duplication"
+#include "header.h"
 
 extern const int64_t kJniZeroHashmoduleWhole = -7714383706413312501LL;
 extern const int64_t kJniZeroHashmodulePriority = 0LL;
@@ -56,8 +49,6 @@
 }  // namespace
 
 // Main Register Function.
-namespace {
-
 bool RegisterNatives(JNIEnv* env) {
   // Register natives in a proxy.
   if (!RegisterNative_J_module_1N(env)) {
@@ -67,7 +58,3 @@
   return true;
 }
 
-}  // namespace
-
-#pragma clang diagnostic pop
-#endif  // TEMP_DIR_HEADER_
diff --git a/test/golden/testModulesWithMultiplexing-Final.h.golden b/test/golden/testModulesWithMultiplexing-Final.h.golden
new file mode 100644
index 0000000..4c39fe8
--- /dev/null
+++ b/test/golden/testModulesWithMultiplexing-Final.h.golden
@@ -0,0 +1,9 @@
+// This file was generated by
+//     //third_party/jni_zero/jni_zero.py
+#ifndef TEMP_DIR_HEADER_
+#define TEMP_DIR_HEADER_
+
+#include <jni.h>
+
+bool RegisterNatives(JNIEnv* env);
+#endif  // TEMP_DIR_HEADER_
diff --git a/test/golden/testMultiplexing-Registration.h.golden b/test/golden/testMultiplexing-Final.cc.golden
similarity index 96%
rename from test/golden/testMultiplexing-Registration.h.golden
rename to test/golden/testMultiplexing-Final.cc.golden
index 5772729..db7737d 100644
--- a/test/golden/testMultiplexing-Registration.h.golden
+++ b/test/golden/testMultiplexing-Final.cc.golden
@@ -3,17 +3,10 @@
 // For
 //     J.N
 
-#ifndef TEMP_DIR_HEADER_
-#define TEMP_DIR_HEADER_
-
 #include <iterator>
 
 #include "overridden/jni_zero_internal.h"
-
-// Field, class, and method accessors use inline functions with static
-// variables for caching.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunique-object-duplication"
+#include "header.h"
 
 extern const int64_t kJniZeroHashWhole = 3865356290363738592LL;
 extern const int64_t kJniZeroHashPriority = 0LL;
@@ -248,8 +241,6 @@
 }  // namespace
 
 // Main Register Function.
-namespace {
-
 bool RegisterNatives(JNIEnv* env) {
   // Register natives in a proxy.
   if (!RegisterNative_J_N(env)) {
@@ -259,7 +250,3 @@
   return true;
 }
 
-}  // namespace
-
-#pragma clang diagnostic pop
-#endif  // TEMP_DIR_HEADER_
diff --git a/test/golden/testMultiplexing-Final.h.golden b/test/golden/testMultiplexing-Final.h.golden
new file mode 100644
index 0000000..4c39fe8
--- /dev/null
+++ b/test/golden/testMultiplexing-Final.h.golden
@@ -0,0 +1,9 @@
+// This file was generated by
+//     //third_party/jni_zero/jni_zero.py
+#ifndef TEMP_DIR_HEADER_
+#define TEMP_DIR_HEADER_
+
+#include <jni.h>
+
+bool RegisterNatives(JNIEnv* env);
+#endif  // TEMP_DIR_HEADER_
diff --git a/test/golden/testPackagePrefixWithManualRegistration-Registration.h.golden b/test/golden/testPackagePrefixWithManualRegistration-Final.cc.golden
similarity index 96%
rename from test/golden/testPackagePrefixWithManualRegistration-Registration.h.golden
rename to test/golden/testPackagePrefixWithManualRegistration-Final.cc.golden
index d1c7a33..d7c05ea 100644
--- a/test/golden/testPackagePrefixWithManualRegistration-Registration.h.golden
+++ b/test/golden/testPackagePrefixWithManualRegistration-Final.cc.golden
@@ -3,17 +3,10 @@
 // For
 //     this.is.a.package.prefix.org.jni_zero.GEN_JNI
 
-#ifndef TEMP_DIR_HEADER_
-#define TEMP_DIR_HEADER_
-
 #include <iterator>
 
 #include "overridden/jni_zero_internal.h"
-
-// Field, class, and method accessors use inline functions with static
-// variables for caching.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunique-object-duplication"
+#include "header.h"
 
 // Forward Declarations.
 JNI_ZERO_BOUNDARY_EXPORT jobject Java_this_is_a_package_prefix_org_jni_1zero_GEN_1JNI_org_1jni_11zero_1SampleForAnnotationProcessor_1bar(
@@ -209,8 +202,6 @@
 }  // namespace
 
 // Main Register Function.
-namespace {
-
 bool RegisterNatives(JNIEnv* env) {
   // Register natives in a proxy.
   if (!RegisterNative_this_is_a_package_prefix_org_jni_1zero_GEN_1JNI(env)) {
@@ -220,7 +211,3 @@
   return true;
 }
 
-}  // namespace
-
-#pragma clang diagnostic pop
-#endif  // TEMP_DIR_HEADER_
diff --git a/test/golden/testPackagePrefixWithManualRegistration-Final.h.golden b/test/golden/testPackagePrefixWithManualRegistration-Final.h.golden
new file mode 100644
index 0000000..4c39fe8
--- /dev/null
+++ b/test/golden/testPackagePrefixWithManualRegistration-Final.h.golden
@@ -0,0 +1,9 @@
+// This file was generated by
+//     //third_party/jni_zero/jni_zero.py
+#ifndef TEMP_DIR_HEADER_
+#define TEMP_DIR_HEADER_
+
+#include <jni.h>
+
+bool RegisterNatives(JNIEnv* env);
+#endif  // TEMP_DIR_HEADER_
diff --git a/test/golden/testPackagePrefixWithManualRegistrationWithMultiplexing-Registration.h.golden b/test/golden/testPackagePrefixWithManualRegistrationWithMultiplexing-Final.cc.golden
similarity index 96%
rename from test/golden/testPackagePrefixWithManualRegistrationWithMultiplexing-Registration.h.golden
rename to test/golden/testPackagePrefixWithManualRegistrationWithMultiplexing-Final.cc.golden
index 6b91b4a..dbd689d 100644
--- a/test/golden/testPackagePrefixWithManualRegistrationWithMultiplexing-Registration.h.golden
+++ b/test/golden/testPackagePrefixWithManualRegistrationWithMultiplexing-Final.cc.golden
@@ -3,17 +3,10 @@
 // For
 //     this.is.a.package.prefix.J.N
 
-#ifndef TEMP_DIR_HEADER_
-#define TEMP_DIR_HEADER_
-
 #include <iterator>
 
 #include "overridden/jni_zero_internal.h"
-
-// Field, class, and method accessors use inline functions with static
-// variables for caching.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunique-object-duplication"
+#include "header.h"
 
 extern const int64_t kJniZeroHashWhole = 3865356290363738592LL;
 extern const int64_t kJniZeroHashPriority = 0LL;
@@ -251,8 +244,6 @@
 }  // namespace
 
 // Main Register Function.
-namespace {
-
 bool RegisterNatives(JNIEnv* env) {
   // Register natives in a proxy.
   if (!RegisterNative_this_is_a_package_prefix_J_N(env)) {
@@ -262,7 +253,3 @@
   return true;
 }
 
-}  // namespace
-
-#pragma clang diagnostic pop
-#endif  // TEMP_DIR_HEADER_
diff --git a/test/golden/testPackagePrefixWithManualRegistrationWithMultiplexing-Final.h.golden b/test/golden/testPackagePrefixWithManualRegistrationWithMultiplexing-Final.h.golden
new file mode 100644
index 0000000..4c39fe8
--- /dev/null
+++ b/test/golden/testPackagePrefixWithManualRegistrationWithMultiplexing-Final.h.golden
@@ -0,0 +1,9 @@
+// This file was generated by
+//     //third_party/jni_zero/jni_zero.py
+#ifndef TEMP_DIR_HEADER_
+#define TEMP_DIR_HEADER_
+
+#include <jni.h>
+
+bool RegisterNatives(JNIEnv* env);
+#endif  // TEMP_DIR_HEADER_
diff --git a/test/golden/testPackagePrefixWithMultiplexing-Registration.h.golden b/test/golden/testPackagePrefixWithMultiplexing-Final.cc.golden
similarity index 95%
rename from test/golden/testPackagePrefixWithMultiplexing-Registration.h.golden
rename to test/golden/testPackagePrefixWithMultiplexing-Final.cc.golden
index 65c601c..80ab352 100644
--- a/test/golden/testPackagePrefixWithMultiplexing-Registration.h.golden
+++ b/test/golden/testPackagePrefixWithMultiplexing-Final.cc.golden
@@ -3,18 +3,10 @@
 // For
 //     this.is.a.package.prefix.J.N
 
-#ifndef TEMP_DIR_HEADER_
-#define TEMP_DIR_HEADER_
-
 #include <iterator>
 
 #include "overridden/jni_zero_internal.h"
 
-// Field, class, and method accessors use inline functions with static
-// variables for caching.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunique-object-duplication"
-
 extern const int64_t kJniZeroHashWhole = 3865356290363738592LL;
 extern const int64_t kJniZeroHashPriority = 0LL;
 
@@ -213,5 +205,3 @@
   return Muxed_org_jni_1zero_SampleForAnnotationProcessor_testSpecialTypes(env, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18);
 }
 
-#pragma clang diagnostic pop
-#endif  // TEMP_DIR_HEADER_
diff --git a/test/golden/testPriorityRegistration-Registration.h.golden b/test/golden/testPriorityRegistration-Final.cc.golden
similarity index 94%
rename from test/golden/testPriorityRegistration-Registration.h.golden
rename to test/golden/testPriorityRegistration-Final.cc.golden
index 3132459..ecab5a6 100644
--- a/test/golden/testPriorityRegistration-Registration.h.golden
+++ b/test/golden/testPriorityRegistration-Final.cc.golden
@@ -3,18 +3,10 @@
 // For
 //     J.N
 
-#ifndef TEMP_DIR_HEADER_
-#define TEMP_DIR_HEADER_
-
 #include <iterator>
 
 #include "overridden/jni_zero_internal.h"
 
-// Field, class, and method accessors use inline functions with static
-// variables for caching.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunique-object-duplication"
-
 extern const int64_t kJniZeroHashWhole = -7874644414477415949LL;
 extern const int64_t kJniZeroHashPriority = 4128442159926458844LL;
 
@@ -189,5 +181,3 @@
   }
 }
 
-#pragma clang diagnostic pop
-#endif  // TEMP_DIR_HEADER_
diff --git a/test/golden/testPriorityRegistrationPart2-Registration.h.golden b/test/golden/testPriorityRegistrationPart2-Final.cc.golden
similarity index 76%
rename from test/golden/testPriorityRegistrationPart2-Registration.h.golden
rename to test/golden/testPriorityRegistrationPart2-Final.cc.golden
index 4a42bb3..b0e0bed 100644
--- a/test/golden/testPriorityRegistrationPart2-Registration.h.golden
+++ b/test/golden/testPriorityRegistrationPart2-Final.cc.golden
@@ -3,18 +3,10 @@
 // For
 //     J.N
 
-#ifndef TEMP_DIR_HEADER_
-#define TEMP_DIR_HEADER_
-
 #include <iterator>
 
 #include "overridden/jni_zero_internal.h"
 
-// Field, class, and method accessors use inline functions with static
-// variables for caching.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunique-object-duplication"
-
 extern const int64_t kJniZeroHashWhole = 4128442159926458844LL;
 extern const int64_t kJniZeroHashPriority = 1LL;
 
@@ -46,5 +38,3 @@
   }
 }
 
-#pragma clang diagnostic pop
-#endif  // TEMP_DIR_HEADER_
diff --git a/test/integration_tests.py b/test/integration_tests.py
index 633cd97..1d2d867 100755
--- a/test/integration_tests.py
+++ b/test/integration_tests.py
@@ -76,6 +76,8 @@
     self.add_stubs_for_missing_native = False
     self.include_test_only = False
     self.manual_jni_registration = False
+    self.header_path = None
+    self.impl_path = None
     self.remove_uncalled_methods = False
     self.needs_javap = is_javap or is_gen_register_natives
     self.__dict__.update(kwargs)
@@ -114,6 +116,8 @@
       ret.append('--add-stubs-for-missing-native')
     if self.header_path:
       ret += ['--header-path', self.header_path]
+    if self.impl_path:
+      ret += ['--impl-path', self.impl_path]
     if self.linker_script_path:
       ret += ['--linker-script-path', self.linker_script_path]
     if self.register_natives_name:
@@ -183,14 +187,14 @@
 
     self.AssertGoldenTextEquals('\n'.join(expected_contents), golden_path)
 
-  def _TestEndToEndGeneration(self,
-                              input_files,
-                              *,
-                              srcjar=False,
-                              generate_placeholders=False,
-                              enable_jni_multiplexing=False,
-                              per_file_natives=False,
-                              **kwargs):
+  def _TestGenerateJni(self,
+                       input_files,
+                       *,
+                       srcjar=False,
+                       generate_placeholders=False,
+                       enable_jni_multiplexing=False,
+                       per_file_natives=False,
+                       **kwargs):
     is_javap = input_files[0].endswith('.class')
     golden_name = self._testMethodName
     options = CliOptions(is_javap=is_javap, **kwargs)
@@ -257,13 +261,13 @@
         self._CheckPlaceholderSrcjarGolden(placeholder_srcjar_path,
                                            placeholder_srcjar_golden)
 
-  def _TestEndToEndRegistration(self,
-                                input_files,
-                                golden_name=None,
-                                src_files_for_asserts_and_stubs=None,
-                                priority_java_files=None,
-                                inspection_func=None,
-                                **kwargs):
+  def _TestGenerateFinal(self,
+                         input_files,
+                         golden_name=None,
+                         src_files_for_asserts_and_stubs=None,
+                         priority_java_files=None,
+                         inspection_func=None,
+                         **kwargs):
     golden_name = golden_name or self._testMethodName
     options = CliOptions(is_final=True, **kwargs)
     dir_prefix, file_prefix = _MakePrefixes(options)
@@ -275,8 +279,11 @@
       name_to_goldens[f'{dir_prefix}J/{file_prefix}N.java'] = (
           f'{golden_name}-Final-N.java.golden')
     header_golden = None
-    if options.use_proxy_hash or options.manual_jni_registration or options.enable_jni_multiplexing:
-      header_golden = f'{golden_name}-Registration.h.golden'
+    if options.manual_jni_registration:
+      header_golden = f'{golden_name}-Final.h.golden'
+    impl_golden = None
+    if options.manual_jni_registration or options.enable_jni_multiplexing:
+      impl_golden = f'{golden_name}-Final.cc.golden'
 
     with tempfile.TemporaryDirectory() as tdir:
       native_sources = [os.path.join(_JAVA_SRC_DIR, f) for f in input_files]
@@ -310,6 +317,9 @@
 
       srcjar_path = os.path.join(tdir, 'srcjar.jar')
       cmd += ['--srcjar-path', srcjar_path]
+      if impl_golden:
+        impl_path = os.path.join(tdir, 'impl.cc')
+        cmd += ['--impl-path', impl_path]
       if header_golden:
         header_path = os.path.join(tdir, 'header.h')
         cmd += ['--header-path', header_path]
@@ -319,10 +329,14 @@
 
       self._CheckSrcjarGoldens(srcjar_path, name_to_goldens)
 
+      if impl_golden:
+        with open(impl_path, 'r') as f:
+          contents = f.read().replace(
+              tdir.replace('/', '_').upper(), 'TEMP_DIR')
+          self.AssertGoldenTextEquals(contents, impl_golden)
+
       if header_golden:
         with open(header_path, 'r') as f:
-          # Temp directory will cause some diffs each time we run if we don't
-          # normalize.
           contents = f.read().replace(
               tdir.replace('/', '_').upper(), 'TEMP_DIR')
           self.AssertGoldenTextEquals(contents, header_golden)
@@ -407,50 +421,50 @@
 class Tests(BaseTest):
 
   def testGenerics(self):
-    self._TestEndToEndGeneration(['SampleGenerics.java'], srcjar=True)
+    self._TestGenerateJni(['SampleGenerics.java'], srcjar=True)
 
   def testBidirectionalClass(self):
-    self._TestEndToEndGeneration(['SampleForTests.java'], srcjar=True)
-    self._TestEndToEndRegistration(['SampleForTests.java'])
+    self._TestGenerateJni(['SampleForTests.java'], srcjar=True)
+    self._TestGenerateFinal(['SampleForTests.java'])
 
   def testFromClassFile(self):
-    self._TestEndToEndGeneration(['JavapClass.class'])
+    self._TestGenerateJni(['JavapClass.class'])
 
   def testJavaUtilList(self):
-    self._TestEndToEndGeneration(['List.class'])
+    self._TestGenerateJni(['List.class'])
 
   def testUniqueAnnotations(self):
-    self._TestEndToEndGeneration(['SampleUniqueAnnotations.java'], srcjar=True)
+    self._TestGenerateJni(['SampleUniqueAnnotations.java'], srcjar=True)
 
   def testPerFileNatives(self):
-    self._TestEndToEndGeneration(['SampleForAnnotationProcessor.java'],
-                                 srcjar=True,
-                                 per_file_natives=True)
+    self._TestGenerateJni(['SampleForAnnotationProcessor.java'],
+                          srcjar=True,
+                          per_file_natives=True)
 
   def testEndToEndProxyHashed(self):
-    self._TestEndToEndGeneration(['SampleForAnnotationProcessor.java'],
-                                 srcjar=True,
-                                 generate_placeholders=True)
-    self._TestEndToEndRegistration(['SampleForAnnotationProcessor.java'],
-                                   use_proxy_hash=True)
+    self._TestGenerateJni(['SampleForAnnotationProcessor.java'],
+                          srcjar=True,
+                          generate_placeholders=True)
+    self._TestGenerateFinal(['SampleForAnnotationProcessor.java'],
+                            use_proxy_hash=True)
 
   def testEndToEndManualRegistration(self):
-    self._TestEndToEndRegistration(['SampleForAnnotationProcessor.java'],
-                                   manual_jni_registration=True)
+    self._TestGenerateFinal(['SampleForAnnotationProcessor.java'],
+                            manual_jni_registration=True)
 
   def testEndToEndProxyJniWithModules(self):
-    self._TestEndToEndGeneration(['SampleModule.java'],
-                                 srcjar=True,
-                                 use_proxy_hash=True,
-                                 module_name='module')
-    self._TestEndToEndRegistration(
+    self._TestGenerateJni(['SampleModule.java'],
+                          srcjar=True,
+                          use_proxy_hash=True,
+                          module_name='module')
+    self._TestGenerateFinal(
         ['SampleForAnnotationProcessor.java', 'SampleModule.java'],
         use_proxy_hash=True,
         manual_jni_registration=True,
         module_name='module')
 
   def testModulesWithMultiplexing(self):
-    self._TestEndToEndRegistration(
+    self._TestGenerateFinal(
         ['SampleForAnnotationProcessor.java', 'SampleModule.java'],
         enable_jni_multiplexing=True,
         manual_jni_registration=True,
@@ -462,11 +476,10 @@
         'TinySample.java', 'SampleProxyEdgeCases.java'
     ]
     extra_input_java_files = ['TinySample2.java']
-    self._TestEndToEndRegistration(
-        input_java_files + extra_input_java_files,
-        src_files_for_asserts_and_stubs=stubs_java_files,
-        add_stubs_for_missing_native=True,
-        remove_uncalled_methods=True)
+    self._TestGenerateFinal(input_java_files + extra_input_java_files,
+                            src_files_for_asserts_and_stubs=stubs_java_files,
+                            add_stubs_for_missing_native=True,
+                            remove_uncalled_methods=True)
 
   def testPriorityRegistration(self):
     input_java_files = [
@@ -479,94 +492,95 @@
     hash_holder = []
 
     def inspection_func(tdir):
-      header_path = os.path.join(tdir, 'header.h')
-      header_text = pathlib.Path(header_path).read_text()
-      whole = re.findall(r'HashWhole.*?= (.*?);', header_text)[0]
-      priority = re.findall(r'HashPriority.*?= (.*?);', header_text)[0]
+      impl_path = os.path.join(tdir, 'impl.cc')
+      impl_text = pathlib.Path(impl_path).read_text()
+      whole = re.findall(r'HashWhole.*?= (.*?);', impl_text)[0]
+      priority = re.findall(r'HashPriority.*?= (.*?);', impl_text)[0]
       hash_holder.append((whole, priority))
 
-    self._TestEndToEndRegistration(input_java_files,
-                                   priority_java_files=priority_java_files,
-                                   inspection_func=inspection_func,
-                                   enable_jni_multiplexing=True)
+    self._TestGenerateFinal(input_java_files,
+                            priority_java_files=priority_java_files,
+                            inspection_func=inspection_func,
+                            enable_jni_multiplexing=True)
 
-    self._TestEndToEndRegistration(priority_java_files,
-                                   golden_name='testPriorityRegistrationPart2',
-                                   priority_java_files=[],
-                                   inspection_func=inspection_func,
-                                   enable_jni_multiplexing=True)
+    self._TestGenerateFinal(priority_java_files,
+                            golden_name='testPriorityRegistrationPart2',
+                            priority_java_files=[],
+                            inspection_func=inspection_func,
+                            enable_jni_multiplexing=True)
     self.assertEqual(hash_holder[0][1], hash_holder[1][0])
 
   def testFullStubs(self):
-    self._TestEndToEndRegistration(
-        [],
-        src_files_for_asserts_and_stubs=['TinySample.java'],
-        add_stubs_for_missing_native=True)
+    self._TestGenerateFinal([],
+                            src_files_for_asserts_and_stubs=['TinySample.java'],
+                            add_stubs_for_missing_native=True)
 
   def testForTestingKeptHash(self):
     input_java_file = 'SampleProxyEdgeCases.java'
-    self._TestEndToEndGeneration([input_java_file], srcjar=True)
-    self._TestEndToEndRegistration([input_java_file],
-                                   use_proxy_hash=True,
-                                   include_test_only=True)
+    self._TestGenerateJni([input_java_file], srcjar=True)
+    self._TestGenerateFinal([input_java_file],
+                            use_proxy_hash=True,
+                            include_test_only=True)
 
   def testForTestingRemovedHash(self):
-    self._TestEndToEndRegistration(['SampleProxyEdgeCases.java'],
-                                   use_proxy_hash=True,
-                                   include_test_only=False)
+    self._TestGenerateFinal(['SampleProxyEdgeCases.java'],
+                            use_proxy_hash=True,
+                            include_test_only=False)
 
   def testForTestingKeptMultiplexing(self):
     input_java_file = 'SampleProxyEdgeCases.java'
-    self._TestEndToEndGeneration([input_java_file], enable_jni_multiplexing=True, srcjar=True)
-    self._TestEndToEndRegistration([input_java_file],
-                                   enable_jni_multiplexing=True,
-                                   include_test_only=True)
+    self._TestGenerateJni([input_java_file],
+                          enable_jni_multiplexing=True,
+                          srcjar=True)
+    self._TestGenerateFinal([input_java_file],
+                            enable_jni_multiplexing=True,
+                            include_test_only=True)
 
   def testForTestingRemovedMultiplexing(self):
-    self._TestEndToEndRegistration(['SampleProxyEdgeCases.java'],
-                                   enable_jni_multiplexing=True,
-                                   include_test_only=False)
+    self._TestGenerateFinal(['SampleProxyEdgeCases.java'],
+                            enable_jni_multiplexing=True,
+                            include_test_only=False)
 
   def testPackagePrefixGenerator(self):
-    self._TestEndToEndGeneration(['SampleForTests.java'],
-                                 srcjar=True,
-                                 package_prefix='this.is.a.package.prefix',
-                                 generate_placeholders=True)
+    self._TestGenerateJni(['SampleForTests.java'],
+                          srcjar=True,
+                          package_prefix='this.is.a.package.prefix',
+                          generate_placeholders=True)
 
   def testPackagePrefixWithFilter(self):
-    self._TestEndToEndGeneration(['SampleForTests.java'],
-                                 srcjar=True,
-                                 package_prefix='this.is.a.package.prefix',
-                                 package_prefix_filter='org.jni_zero')
+    self._TestGenerateJni(['SampleForTests.java'],
+                          srcjar=True,
+                          package_prefix='this.is.a.package.prefix',
+                          package_prefix_filter='org.jni_zero')
 
   def testPackagePrefixWithManualRegistration(self):
-    self._TestEndToEndRegistration(['SampleForAnnotationProcessor.java'],
-                                   package_prefix='this.is.a.package.prefix',
-                                   manual_jni_registration=True)
+    self._TestGenerateFinal(['SampleForAnnotationProcessor.java'],
+                            package_prefix='this.is.a.package.prefix',
+                            manual_jni_registration=True)
 
   def testPackagePrefixWithMultiplexing(self):
-    self._TestEndToEndRegistration(['SampleForAnnotationProcessor.java'],
-                                   package_prefix='this.is.a.package.prefix',
-                                   enable_jni_multiplexing=True)
+    self._TestGenerateFinal(['SampleForAnnotationProcessor.java'],
+                            package_prefix='this.is.a.package.prefix',
+                            enable_jni_multiplexing=True)
 
   def testPackagePrefixWithManualRegistrationWithMultiplexing(self):
-    self._TestEndToEndRegistration(['SampleForAnnotationProcessor.java'],
-                                   package_prefix='this.is.a.package.prefix',
-                                   enable_jni_multiplexing=True,
-                                   manual_jni_registration=True)
+    self._TestGenerateFinal(['SampleForAnnotationProcessor.java'],
+                            package_prefix='this.is.a.package.prefix',
+                            enable_jni_multiplexing=True,
+                            manual_jni_registration=True)
 
   def testPlaceholdersOverlapping(self):
-    self._TestEndToEndGeneration([
+    self._TestGenerateJni([
         'TinySample.java',
         'extrapackage/ImportsTinySample.java',
     ],
-                                 srcjar=True,
-                                 generate_placeholders=True)
+                          srcjar=True,
+                          generate_placeholders=True)
 
   def testMultiplexing(self):
-    self._TestEndToEndRegistration(['SampleForAnnotationProcessor.java'],
-                                   enable_jni_multiplexing=True,
-                                   manual_jni_registration=True)
+    self._TestGenerateFinal(['SampleForAnnotationProcessor.java'],
+                            enable_jni_multiplexing=True,
+                            manual_jni_registration=True)
 
   def testGenRegisterNatives(self):
     with tempfile.TemporaryDirectory() as tdir:
@@ -606,8 +620,8 @@
           tdir.replace('/', '_').upper(), 'TEMP_DIR')
       linker_content = pathlib.Path(linker_script_path).read_text()
 
-      self.AssertGoldenTextEquals(
-          header_content, 'testGenRegisterNatives-Registration.h.golden')
+      self.AssertGoldenTextEquals(header_content,
+                                  'testGenRegisterNatives-Final.h.golden')
       self.AssertGoldenTextEquals(
           linker_content, 'testGenRegisterNatives-LinkerScript.txt.golden')