blob: 8d4946322deac9b81a90ff149ecc37d29024a4ef [file]
#!/usr/bin/env python3
# Copyright (C) 2026 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import base64
import os
import textwrap
# The JVM specification strictly limits the bytecode of any single method (including
# static initializers `<clinit>`) to 65535 bytes. A simple `{ (byte)x, ... }` byte array
# generates push and store bytecode instructions per element. While the absolute
# mathematical threshold for standard javac is ~10,943 elements, we conservatively cap
# this at 8192 (8 KB) to guarantee safety across different compiler versions and pipelines.
JAVA_ARRAY_BYTE_LIMIT = 8192
TEMPLATE = """/*
* Copyright (C) 2026 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package {package};
/**
* Autogenerated by java_blob_emitter.py
*/
public class {classname} {{
{class_body}
}}
"""
def generate_code(package: str, classname: str, data: bytes) -> str:
# Store data as byte array literal
if len(data) <= JAVA_ARRAY_BYTE_LIMIT:
bytes = [f"(byte)0x{b:02x}" for b in data]
bytes_joined = ", ".join(bytes)
bytes_wrapped = textwrap.fill(
bytes_joined,
width=100,
initial_indent=" ",
subsequent_indent=" ")
class_body = " private static final byte[] BYTES = {\n"
class_body += bytes_wrapped + "\n"
class_body += " };\n\n"
class_body += " public static byte[] getBytes() {\n"
class_body += " return BYTES;\n"
class_body += " }"
# Store data as base64-encoded string
else:
bytes_base64 = base64.b64encode(data).decode('utf-8')
class_body = f" private static final String BYTES_BASE64 = \"{bytes_base64}\";\n\n"
class_body += " public static byte[] getBytes() {\n"
class_body += " return android.util.Base64.decode(BYTES_BASE64, android.util.Base64.DEFAULT);\n"
class_body += " }"
return TEMPLATE.format(
package=package, classname=classname, class_body=class_body)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--output', required=True)
parser.add_argument('--package', required=True)
parser.add_argument('input')
args = parser.parse_args()
with open(args.input, 'rb') as f:
data = f.read()
classname = os.path.basename(args.output).split('.', 1)[0]
code = generate_code(args.package, classname, data)
with open(args.output, 'w') as f:
f.write(code)
if __name__ == '__main__':
main()