Expose `cc_helper.tokenize` and optionally use it in `java_helper.tokenize_javacopts`

For large option sets, such as those involved in java compilation, `ctx.tokenize()` is up to 100x faster than the pure Starlark implementation. We should resort to using the slow path only in the cases where `ctx` is unavailable (such as when performing a `map_each` in `args.add_all`).

PiperOrigin-RevId: 585977311
Change-Id: I547c6dbc1a37ef15494a9b398a597a4246c08ea9
diff --git a/src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl b/src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl
index e3376fc..6e8e360 100644
--- a/src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl
+++ b/src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl
@@ -1266,4 +1266,5 @@
     proto_output_root = _proto_output_root,
     cc_toolchain_build_variables = _cc_toolchain_build_variables,
     package_source_root = _package_source_root,
+    tokenize = _tokenize,
 )
diff --git a/src/main/starlark/builtins_bzl/common/java/java_helper.bzl b/src/main/starlark/builtins_bzl/common/java/java_helper.bzl
index 7f71e33..617b360 100644
--- a/src/main/starlark/builtins_bzl/common/java/java_helper.bzl
+++ b/src/main/starlark/builtins_bzl/common/java/java_helper.bzl
@@ -377,25 +377,39 @@
             return "'" + s.replace("'", "'\\''") + "'"
     return s
 
-def _tokenize_javacopts(ctx, opts):
+def _tokenize_javacopts(ctx = None, opts = []):
     """Tokenizes a list or depset of options to a list.
 
     Iff opts is a depset, we reverse the flattened list to ensure right-most
     duplicates are preserved in their correct position.
 
+    If the ctx parameter is omitted, a slow, but pure Starlark, implementation
+    of shell tokenization is used. Otherwise, tokenization is performed using
+    ctx.tokenize() which has significantly better performance (up to 100x for
+    large options lists).
+
     Args:
-        ctx: (RuleContext) the rule context
+        ctx: (RuleContext|None) the rule context
         opts: (depset[str]|[str]) the javac options to tokenize
     Returns:
         [str] list of tokenized options
     """
     if hasattr(opts, "to_list"):
         opts = reversed(opts.to_list())
-    return [
-        token
-        for opt in opts
-        for token in ctx.tokenize(opt)
-    ]
+    if ctx:
+        return [
+            token
+            for opt in opts
+            for token in ctx.tokenize(opt)
+        ]
+    else:
+        # slow, but pure Starlark implementation
+        result = []
+        for opt in opts:
+            tokens = []
+            cc_helper.tokenize(tokens, opt)
+            result.extend(tokens)
+        return result
 
 def _detokenize_javacopts(opts):
     """Detokenizes a list of options to a depset.