blob: 8f9f21856709f467cf30445f8a0ec0a9420044b4 [file] [log] [blame]
#!/usr/bin/env python3
# Copyright 2017 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Generates GEN_JNI.java (or N.java) and helper for manual JNI registration.
Creates a header file with two static functions: RegisterMainDexNatives() and
RegisterNonMainDexNatives(). Together, these will use manual JNI registration
to register all native methods that exist within an application."""
import argparse
import collections
import functools
import hashlib
import multiprocessing
import os
import string
import sys
import zipfile
import jni_generator
from util import build_utils
# All but FULL_CLASS_NAME, which is used only for sorting.
MERGEABLE_KEYS = [
'CLASS_PATH_DECLARATIONS',
'FORWARD_DECLARATIONS',
'JNI_NATIVE_METHOD',
'JNI_NATIVE_METHOD_ARRAY',
'PROXY_NATIVE_SIGNATURES',
'FORWARDING_PROXY_METHODS',
'PROXY_NATIVE_METHOD_ARRAY',
'PROXY_NATIVE_METHOD_ARRAY_MAIN_DEX',
'REGISTER_MAIN_DEX_NATIVES',
'REGISTER_NON_MAIN_DEX_NATIVES',
]
def _Generate(options, java_file_paths):
"""Generates files required to perform JNI registration.
Generates a srcjar containing a single class, GEN_JNI, that contains all
native method declarations.
Optionally generates a header file that provides functions
(RegisterMainDexNatives and RegisterNonMainDexNatives) to perform
JNI registration.
Args:
options: arguments from the command line
java_file_paths: A list of java file paths.
"""
# Without multiprocessing, script takes ~13 seconds for chrome_public_apk
# on a z620. With multiprocessing, takes ~2 seconds.
results = collections.defaultdict(list)
with multiprocessing.Pool() as pool:
for d in pool.imap_unordered(functools.partial(_DictForPath, options),
java_file_paths):
if d:
results[d['MODULE_NAME']].append(d)
combined_dicts = collections.defaultdict(dict)
for module_name, module_results in results.items():
# Sort to make output deterministic.
module_results.sort(key=lambda d: d['FULL_CLASS_NAME'])
combined_dict = combined_dicts[module_name]
for key in MERGEABLE_KEYS:
combined_dict[key] = ''.join(d.get(key, '') for d in module_results)
# PROXY_NATIVE_SIGNATURES and PROXY_NATIVE_METHOD_ARRAY_MAIN_DEX will have
# duplicates for JNI multiplexing since all native methods with similar
# signatures map to the same proxy. Similarly, there may be multiple switch
# case entries for the same proxy signatures.
if options.enable_jni_multiplexing:
proxy_signatures_list = sorted(
set(combined_dict['PROXY_NATIVE_SIGNATURES'].split('\n')))
combined_dict['PROXY_NATIVE_SIGNATURES'] = '\n'.join(
signature for signature in proxy_signatures_list)
proxy_native_array_list = sorted(
set(combined_dict['PROXY_NATIVE_METHOD_ARRAY_MAIN_DEX'].split(
'},\n')))
combined_dict['PROXY_NATIVE_METHOD_ARRAY_MAIN_DEX'] = '},\n'.join(
p for p in proxy_native_array_list if p != '') + '}'
signature_to_cases = collections.defaultdict(list)
for d in module_results:
for signature, cases in d['SIGNATURE_TO_CASES'].items():
signature_to_cases[signature].extend(cases)
combined_dict['FORWARDING_CALLS'] = _AddForwardingCalls(
signature_to_cases, module_name)
if options.header_path:
assert len(
combined_dicts) == 1, 'Cannot output a header for multiple modules'
module_name = next(iter(combined_dicts))
combined_dict = combined_dicts[module_name]
combined_dict['HEADER_GUARD'] = \
os.path.splitext(options.header_path)[0].replace('/', '_').replace('.', '_').upper() + '_'
combined_dict['NAMESPACE'] = options.namespace
header_content = CreateFromDict(options, module_name, combined_dict)
with build_utils.AtomicOutput(options.header_path, mode='w') as f:
f.write(header_content)
with build_utils.AtomicOutput(options.srcjar_path) as f:
with zipfile.ZipFile(f, 'w') as srcjar:
for module_name, combined_dict in combined_dicts.items():
if options.use_proxy_hash or options.enable_jni_multiplexing:
# J/N.java
build_utils.AddToZipHermetic(
srcjar,
'%s.java' %
jni_generator.ProxyHelpers.GetQualifiedClass(True, module_name),
data=CreateProxyJavaFromDict(options, module_name, combined_dict))
# org/chromium/base/natives/GEN_JNI.java
build_utils.AddToZipHermetic(
srcjar,
'%s.java' %
jni_generator.ProxyHelpers.GetQualifiedClass(False, module_name),
data=CreateProxyJavaFromDict(options,
module_name,
combined_dict,
forwarding=True))
else:
# org/chromium/base/natives/GEN_JNI.java
build_utils.AddToZipHermetic(
srcjar,
'%s.java' %
jni_generator.ProxyHelpers.GetQualifiedClass(False, module_name),
data=CreateProxyJavaFromDict(options, module_name, combined_dict))
def _DictForPath(options, path):
with open(path) as f:
contents = jni_generator.RemoveComments(f.read())
if '@JniIgnoreNatives' in contents:
return None
fully_qualified_class = jni_generator.ExtractFullyQualifiedJavaClassName(
path, contents)
natives, module_name = jni_generator.ProxyHelpers.ExtractStaticProxyNatives(
fully_qualified_class=fully_qualified_class,
contents=contents,
ptr_type='long',
include_test_only=options.include_test_only)
natives += jni_generator.ExtractNatives(contents, 'long')
if len(natives) == 0:
return None
# The namespace for the content is separate from the namespace for the
# generated header file.
content_namespace = jni_generator.ExtractJNINamespace(contents)
jni_params = jni_generator.JniParams(fully_qualified_class)
jni_params.ExtractImportsAndInnerClasses(contents)
is_main_dex = jni_generator.IsMainDexJavaClass(contents)
dict_generator = DictionaryGenerator(options, module_name, content_namespace,
fully_qualified_class, natives,
jni_params, is_main_dex)
return dict_generator.Generate()
def _AddForwardingCalls(signature_to_cases, module_name):
template = string.Template("""
JNI_GENERATOR_EXPORT ${RETURN} Java_${CLASS_NAME}_${PROXY_SIGNATURE}(
JNIEnv* env,
jclass jcaller,
${PARAMS_IN_STUB}) {
switch (switch_num) {
${CASES}
default:
CHECK(false) << "JNI multiplexing function Java_\
${CLASS_NAME}_${PROXY_SIGNATURE} was called with an invalid switch number: "\
<< switch_num;
return${DEFAULT_RETURN};
}
}""")
switch_statements = []
for signature, cases in sorted(signature_to_cases.items()):
return_type, params_list = signature
params_in_stub = _GetJavaToNativeParamsList(params_list)
switch_statements.append(
template.substitute({
'RETURN':
jni_generator.JavaDataTypeToC(return_type),
'CLASS_NAME':
jni_generator.EscapeClassName(
jni_generator.ProxyHelpers.GetQualifiedClass(True,
module_name)),
'PROXY_SIGNATURE':
jni_generator.EscapeClassName(
_GetMultiplexProxyName(return_type, params_list)),
'PARAMS_IN_STUB':
params_in_stub,
'CASES':
''.join(cases),
'DEFAULT_RETURN':
'' if return_type == 'void' else ' {}',
}))
return ''.join(s for s in switch_statements)
def _SetProxyRegistrationFields(options, module_name, registration_dict):
registration_template = string.Template("""\
static const JNINativeMethod kMethods_${ESCAPED_PROXY_CLASS}[] = {
${KMETHODS}
};
namespace {
JNI_REGISTRATION_EXPORT bool ${REGISTRATION_NAME}(JNIEnv* env) {
const int number_of_methods = std::size(kMethods_${ESCAPED_PROXY_CLASS});
base::android::ScopedJavaLocalRef<jclass> native_clazz =
base::android::GetClass(env, "${PROXY_CLASS}");
if (env->RegisterNatives(
native_clazz.obj(),
kMethods_${ESCAPED_PROXY_CLASS},
number_of_methods) < 0) {
jni_generator::HandleRegistrationError(env, native_clazz.obj(), __FILE__);
return false;
}
return true;
}
} // namespace
""")
registration_call = string.Template("""\
// Register natives in a proxy.
if (!${REGISTRATION_NAME}(env)) {
return false;
}
""")
manual_registration = string.Template("""\
// Step 3: Method declarations.
${JNI_NATIVE_METHOD_ARRAY}\
${PROXY_NATIVE_METHOD_ARRAY}\
${JNI_NATIVE_METHOD}
// Step 4: Main dex and non-main dex registration functions.
namespace ${NAMESPACE} {
bool RegisterMainDexNatives(JNIEnv* env) {\
${REGISTER_MAIN_DEX_PROXY_NATIVES}
${REGISTER_MAIN_DEX_NATIVES}
return true;
}
bool RegisterNonMainDexNatives(JNIEnv* env) {\
${REGISTER_PROXY_NATIVES}
${REGISTER_NON_MAIN_DEX_NATIVES}
return true;
}
} // namespace ${NAMESPACE}
""")
short_name = options.use_proxy_hash or options.enable_jni_multiplexing
sub_dict = {
'ESCAPED_PROXY_CLASS':
jni_generator.EscapeClassName(
jni_generator.ProxyHelpers.GetQualifiedClass(short_name,
module_name)),
'PROXY_CLASS':
jni_generator.ProxyHelpers.GetQualifiedClass(short_name, module_name),
'KMETHODS':
registration_dict['PROXY_NATIVE_METHOD_ARRAY'],
'REGISTRATION_NAME':
jni_generator.GetRegistrationFunctionName(
jni_generator.ProxyHelpers.GetQualifiedClass(short_name,
module_name)),
}
if registration_dict['PROXY_NATIVE_METHOD_ARRAY']:
proxy_native_array = registration_template.substitute(sub_dict)
proxy_natives_registration = registration_call.substitute(sub_dict)
else:
proxy_native_array = ''
proxy_natives_registration = ''
if registration_dict['PROXY_NATIVE_METHOD_ARRAY_MAIN_DEX']:
sub_dict['REGISTRATION_NAME'] += 'MAIN_DEX'
sub_dict['ESCAPED_PROXY_CLASS'] += 'MAIN_DEX'
sub_dict['KMETHODS'] = (
registration_dict['PROXY_NATIVE_METHOD_ARRAY_MAIN_DEX'])
proxy_native_array += registration_template.substitute(sub_dict)
main_dex_call = registration_call.substitute(sub_dict)
else:
main_dex_call = ''
registration_dict['PROXY_NATIVE_METHOD_ARRAY'] = proxy_native_array
registration_dict['REGISTER_PROXY_NATIVES'] = proxy_natives_registration
registration_dict['REGISTER_MAIN_DEX_PROXY_NATIVES'] = main_dex_call
if options.manual_jni_registration:
registration_dict['MANUAL_REGISTRATION'] = manual_registration.substitute(
registration_dict)
else:
registration_dict['MANUAL_REGISTRATION'] = ''
def CreateProxyJavaFromDict(options,
module_name,
registration_dict,
forwarding=False):
template = string.Template("""\
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package ${PACKAGE};
// This file is autogenerated by
// base/android/jni_generator/jni_registration_generator.py
// Please do not change its content.
public class ${CLASS_NAME} {
${FIELDS}
${METHODS}
}
""")
is_natives_class = not forwarding and (options.use_proxy_hash
or options.enable_jni_multiplexing)
class_name = jni_generator.ProxyHelpers.GetClass(is_natives_class,
module_name)
package = jni_generator.ProxyHelpers.GetPackage(is_natives_class)
if forwarding or not (options.use_proxy_hash
or options.enable_jni_multiplexing):
fields = string.Template("""\
public static final boolean TESTING_ENABLED = ${TESTING_ENABLED};
public static final boolean REQUIRE_MOCK = ${REQUIRE_MOCK};
""").substitute({
'TESTING_ENABLED': str(options.enable_proxy_mocks).lower(),
'REQUIRE_MOCK': str(options.require_mocks).lower(),
})
else:
fields = ''
if forwarding:
methods = registration_dict['FORWARDING_PROXY_METHODS']
else:
methods = registration_dict['PROXY_NATIVE_SIGNATURES']
return template.substitute({
'CLASS_NAME': class_name,
'FIELDS': fields,
'PACKAGE': package.replace('/', '.'),
'METHODS': methods
})
def CreateFromDict(options, module_name, registration_dict):
"""Returns the content of the header file."""
template = string.Template("""\
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file is autogenerated by
// base/android/jni_generator/jni_registration_generator.py
// Please do not change its content.
#ifndef ${HEADER_GUARD}
#define ${HEADER_GUARD}
#include <jni.h>
#include <iterator>
#include "base/android/jni_generator/jni_generator_helper.h"
#include "base/android/jni_int_wrapper.h"
// Step 1: Forward declarations (classes).
${CLASS_PATH_DECLARATIONS}
// Step 2: Forward declarations (methods).
${FORWARD_DECLARATIONS}
${FORWARDING_CALLS}
${MANUAL_REGISTRATION}
#endif // ${HEADER_GUARD}
""")
_SetProxyRegistrationFields(options, module_name, registration_dict)
if not options.enable_jni_multiplexing:
registration_dict['FORWARDING_CALLS'] = ''
if len(registration_dict['FORWARD_DECLARATIONS']) == 0:
return ''
return template.substitute(registration_dict)
def _GetJavaToNativeParamsList(params_list):
if not params_list:
return 'jlong switch_num'
# Parameters are named after their type, with a unique number per parameter
# type to make sure the names are unique, even within the same types.
params_type_count = collections.defaultdict(int)
params_in_stub = []
for p in params_list:
params_type_count[p] += 1
params_in_stub.append(
'%s %s_param%d' %
(jni_generator.JavaDataTypeToC(p), p.replace(
'[]', '_array').lower(), params_type_count[p]))
return 'jlong switch_num, ' + ', '.join(params_in_stub)
class DictionaryGenerator(object):
"""Generates an inline header file for JNI registration."""
def __init__(self, options, module_name, content_namespace,
fully_qualified_class, natives, jni_params, main_dex):
self.options = options
self.module_name = module_name
self.content_namespace = content_namespace
self.natives = natives
self.proxy_natives = [n for n in natives if n.is_proxy]
self.non_proxy_natives = [n for n in natives if not n.is_proxy]
self.fully_qualified_class = fully_qualified_class
self.jni_params = jni_params
self.class_name = self.fully_qualified_class.split('/')[-1]
self.main_dex = main_dex
self.helper = jni_generator.HeaderFileGeneratorHelper(
self.class_name,
self.module_name,
fully_qualified_class,
options.use_proxy_hash,
enable_jni_multiplexing=options.enable_jni_multiplexing)
self.registration_dict = None
def Generate(self):
self.registration_dict = {
'FULL_CLASS_NAME': self.fully_qualified_class,
'MODULE_NAME': self.module_name
}
self._AddClassPathDeclarations()
self._AddForwardDeclaration()
self._AddJNINativeMethodsArrays()
self._AddProxyNativeMethodKStrings()
self._AddRegisterNativesCalls()
self._AddRegisterNativesFunctions()
self.registration_dict['PROXY_NATIVE_SIGNATURES'] = (''.join(
_MakeProxySignature(self.options, native)
for native in self.proxy_natives))
if self.options.enable_jni_multiplexing:
self._AssignSwitchNumberToNatives()
self._AddCases()
if self.options.use_proxy_hash or self.options.enable_jni_multiplexing:
self.registration_dict['FORWARDING_PROXY_METHODS'] = ('\n'.join(
_MakeForwardingProxy(self.options, self.module_name, native)
for native in self.proxy_natives))
return self.registration_dict
def _SetDictValue(self, key, value):
self.registration_dict[key] = jni_generator.WrapOutput(value)
def _AddClassPathDeclarations(self):
classes = self.helper.GetUniqueClasses(self.natives)
self._SetDictValue(
'CLASS_PATH_DECLARATIONS',
self.helper.GetClassPathLines(classes, declare_only=True))
def _AddForwardDeclaration(self):
"""Add the content of the forward declaration to the dictionary."""
template = string.Template("""\
JNI_GENERATOR_EXPORT ${RETURN} ${STUB_NAME}(
JNIEnv* env,
${PARAMS_IN_STUB});
""")
forward_declaration = ''
for native in self.natives:
value = {
'RETURN': jni_generator.JavaDataTypeToC(native.return_type),
'STUB_NAME': self.helper.GetStubName(native),
'PARAMS_IN_STUB': jni_generator.GetParamsInStub(native),
}
forward_declaration += template.substitute(value)
self._SetDictValue('FORWARD_DECLARATIONS', forward_declaration)
def _AddRegisterNativesCalls(self):
"""Add the body of the RegisterNativesImpl method to the dictionary."""
# Only register if there is at least 1 non-proxy native
if len(self.non_proxy_natives) == 0:
return ''
template = string.Template("""\
if (!${REGISTER_NAME}(env))
return false;
""")
value = {
'REGISTER_NAME':
jni_generator.GetRegistrationFunctionName(self.fully_qualified_class)
}
register_body = template.substitute(value)
if self.main_dex:
self._SetDictValue('REGISTER_MAIN_DEX_NATIVES', register_body)
else:
self._SetDictValue('REGISTER_NON_MAIN_DEX_NATIVES', register_body)
def _AddJNINativeMethodsArrays(self):
"""Returns the implementation of the array of native methods."""
template = string.Template("""\
static const JNINativeMethod kMethods_${JAVA_CLASS}[] = {
${KMETHODS}
};
""")
open_namespace = ''
close_namespace = ''
if self.content_namespace:
parts = self.content_namespace.split('::')
all_namespaces = ['namespace %s {' % ns for ns in parts]
open_namespace = '\n'.join(all_namespaces) + '\n'
all_namespaces = ['} // namespace %s' % ns for ns in parts]
all_namespaces.reverse()
close_namespace = '\n'.join(all_namespaces) + '\n\n'
body = self._SubstituteNativeMethods(template)
if body:
self._SetDictValue('JNI_NATIVE_METHOD_ARRAY', ''.join(
(open_namespace, body, close_namespace)))
def _GetKMethodsString(self, clazz):
ret = []
for native in self.non_proxy_natives:
if (native.java_class_name == clazz
or (not native.java_class_name and clazz == self.class_name)):
ret += [self._GetKMethodArrayEntry(native)]
return '\n'.join(ret)
def _GetKMethodArrayEntry(self, native):
template = string.Template(' { "${NAME}", ${JNI_SIGNATURE}, ' +
'reinterpret_cast<void*>(${STUB_NAME}) },')
name = 'native' + native.name
jni_signature = self.jni_params.Signature(native.params, native.return_type)
stub_name = self.helper.GetStubName(native)
if native.is_proxy:
# Literal name of the native method in the class that contains the actual
# native declaration.
if self.options.enable_jni_multiplexing:
return_type, params_list = native.return_and_signature
class_name = jni_generator.EscapeClassName(
jni_generator.ProxyHelpers.GetQualifiedClass(
True, self.module_name))
proxy_signature = jni_generator.EscapeClassName(
_GetMultiplexProxyName(return_type, params_list))
name = _GetMultiplexProxyName(return_type, params_list)
jni_signature = self.jni_params.Signature(
[jni_generator.Param(datatype='long', name='switch_num')] +
native.params, native.return_type)
stub_name = 'Java_' + class_name + '_' + proxy_signature
elif self.options.use_proxy_hash:
name = native.hashed_proxy_name
else:
name = native.proxy_name
values = {
'NAME': name,
'JNI_SIGNATURE': jni_signature,
'STUB_NAME': stub_name
}
return template.substitute(values)
def _AddProxyNativeMethodKStrings(self):
"""Returns KMethodString for wrapped native methods in all_classes """
if self.main_dex or self.options.enable_jni_multiplexing:
key = 'PROXY_NATIVE_METHOD_ARRAY_MAIN_DEX'
else:
key = 'PROXY_NATIVE_METHOD_ARRAY'
proxy_k_strings = ('\n'.join(
self._GetKMethodArrayEntry(p) for p in self.proxy_natives))
self._SetDictValue(key, proxy_k_strings)
def _SubstituteNativeMethods(self, template):
"""Substitutes NAMESPACE, JAVA_CLASS and KMETHODS in the provided
template."""
ret = []
all_classes = self.helper.GetUniqueClasses(self.natives)
all_classes[self.class_name] = self.fully_qualified_class
for clazz, full_clazz in all_classes.items():
if clazz == jni_generator.ProxyHelpers.GetClass(
self.options.use_proxy_hash or self.options.enable_jni_multiplexing,
self.module_name):
continue
kmethods = self._GetKMethodsString(clazz)
namespace_str = ''
if self.content_namespace:
namespace_str = self.content_namespace + '::'
if kmethods:
values = {
'NAMESPACE': namespace_str,
'JAVA_CLASS': jni_generator.EscapeClassName(full_clazz),
'KMETHODS': kmethods
}
ret += [template.substitute(values)]
if not ret: return ''
return '\n'.join(ret)
def GetJNINativeMethodsString(self):
"""Returns the implementation of the array of native methods."""
template = string.Template("""\
static const JNINativeMethod kMethods_${JAVA_CLASS}[] = {
${KMETHODS}
};
""")
return self._SubstituteNativeMethods(template)
def _AddRegisterNativesFunctions(self):
"""Returns the code for RegisterNatives."""
natives = self._GetRegisterNativesImplString()
if not natives:
return ''
template = string.Template("""\
JNI_REGISTRATION_EXPORT bool ${REGISTER_NAME}(JNIEnv* env) {
${NATIVES}\
return true;
}
""")
values = {
'REGISTER_NAME':
jni_generator.GetRegistrationFunctionName(self.fully_qualified_class),
'NATIVES':
natives
}
self._SetDictValue('JNI_NATIVE_METHOD', template.substitute(values))
def _GetRegisterNativesImplString(self):
"""Returns the shared implementation for RegisterNatives."""
template = string.Template("""\
const int kMethods_${JAVA_CLASS}Size =
std::size(${NAMESPACE}kMethods_${JAVA_CLASS});
if (env->RegisterNatives(
${JAVA_CLASS}_clazz(env),
${NAMESPACE}kMethods_${JAVA_CLASS},
kMethods_${JAVA_CLASS}Size) < 0) {
jni_generator::HandleRegistrationError(env,
${JAVA_CLASS}_clazz(env),
__FILE__);
return false;
}
""")
# Only register if there is a native method not in a proxy,
# since all the proxies will be registered together.
if len(self.non_proxy_natives) != 0:
return self._SubstituteNativeMethods(template)
return ''
def _AssignSwitchNumberToNatives(self):
# The switch number for a native method is a 64-bit long with the first
# bit being a sign digit. The signed two's complement is taken when
# appropriate to make use of negative numbers.
for native in self.proxy_natives:
hashed_long = hashlib.md5(
native.proxy_name.encode('utf-8')).hexdigest()[:16]
switch_num = int(hashed_long, 16)
if (switch_num & 1 << 63):
switch_num -= (1 << 64)
native.switch_num = str(switch_num)
def _AddCases(self):
# Switch cases are grouped together by the same proxy signatures.
template = string.Template("""
case ${SWITCH_NUM}:
return ${STUB_NAME}(env, jcaller${PARAMS});
""")
signature_to_cases = collections.defaultdict(list)
for native in self.proxy_natives:
signature = native.return_and_signature
params = _GetParamsListForMultiplex(signature[1], with_types=False)
values = {
'SWITCH_NUM': native.switch_num,
'STUB_NAME': self.helper.GetStubName(native),
'PARAMS': params,
}
signature_to_cases[signature].append(template.substitute(values))
self.registration_dict['SIGNATURE_TO_CASES'] = signature_to_cases
def _GetParamsListForMultiplex(params_list, with_types):
if not params_list:
return ''
# Parameters are named after their type, with a unique number per parameter
# type to make sure the names are unique, even within the same types.
params_type_count = collections.defaultdict(int)
params = []
for p in params_list:
params_type_count[p] += 1
param_type = p + ' ' if with_types else ''
params.append(
'%s%s_param%d' %
(param_type, p.replace('[]', '_array').lower(), params_type_count[p]))
return ', ' + ', '.join(params)
def _GetMultiplexProxyName(return_type, params_list):
# Proxy signatures for methods are named after their return type and
# parameters to ensure uniqueness, even for the same return types.
params = ''
if params_list:
type_convert_dictionary = {
'[]': 'A',
'byte': 'B',
'char': 'C',
'double': 'D',
'float': 'F',
'int': 'I',
'long': 'J',
'Class': 'L',
'Object': 'O',
'String': 'R',
'short': 'S',
'Throwable': 'T',
'boolean': 'Z',
}
# Parameter types could contain multi-dimensional arrays and every
# instance of [] has to be replaced in the proxy signature name.
for k, v in type_convert_dictionary.items():
params_list = [p.replace(k, v) for p in params_list]
params = '_' + ''.join(p for p in params_list)
return 'resolve_for_' + return_type.replace('[]', '_array').lower() + params
def _MakeForwardingProxy(options, module_name, proxy_native):
template = string.Template("""
public static ${RETURN_TYPE} ${METHOD_NAME}(${PARAMS_WITH_TYPES}) {
${MAYBE_RETURN}${PROXY_CLASS}.${PROXY_METHOD_NAME}(${PARAM_NAMES});
}""")
params_with_types = ', '.join(
'%s %s' % (p.datatype, p.name) for p in proxy_native.params)
param_names = ', '.join(p.name for p in proxy_native.params)
proxy_class = jni_generator.ProxyHelpers.GetQualifiedClass(True, module_name)
if options.enable_jni_multiplexing:
if not param_names:
param_names = proxy_native.switch_num + 'L'
else:
param_names = proxy_native.switch_num + 'L, ' + param_names
return_type, params_list = proxy_native.return_and_signature
proxy_method_name = _GetMultiplexProxyName(return_type, params_list)
else:
proxy_method_name = proxy_native.hashed_proxy_name
return template.substitute({
'RETURN_TYPE':
proxy_native.return_type,
'METHOD_NAME':
proxy_native.proxy_name,
'PARAMS_WITH_TYPES':
params_with_types,
'MAYBE_RETURN':
'' if proxy_native.return_type == 'void' else 'return ',
'PROXY_CLASS':
proxy_class.replace('/', '.'),
'PROXY_METHOD_NAME':
proxy_method_name,
'PARAM_NAMES':
param_names,
})
def _MakeProxySignature(options, proxy_native):
params_with_types = ', '.join('%s %s' % (p.datatype, p.name)
for p in proxy_native.params)
native_method_line = """
public static native ${RETURN} ${PROXY_NAME}(${PARAMS_WITH_TYPES});"""
if options.enable_jni_multiplexing:
# This has to be only one line and without comments because all the proxy
# signatures will be joined, then split on new lines with duplicates removed
# since multiple |proxy_native|s map to the same multiplexed signature.
signature_template = string.Template(native_method_line)
alt_name = None
return_type, params_list = proxy_native.return_and_signature
proxy_name = _GetMultiplexProxyName(return_type, params_list)
params_with_types = 'long switch_num' + _GetParamsListForMultiplex(
params_list, with_types=True)
elif options.use_proxy_hash:
signature_template = string.Template("""
// Original name: ${ALT_NAME}""" + native_method_line)
alt_name = proxy_native.proxy_name
proxy_name = proxy_native.hashed_proxy_name
else:
signature_template = string.Template("""
// Hashed name: ${ALT_NAME}""" + native_method_line)
# We add the prefix that is sometimes used so that codesearch can find it if
# someone searches a full method name from the stacktrace.
alt_name = f'Java_J_N_{proxy_native.hashed_proxy_name}'
proxy_name = proxy_native.proxy_name
return signature_template.substitute({
'ALT_NAME': alt_name,
'RETURN': proxy_native.return_type,
'PROXY_NAME': proxy_name,
'PARAMS_WITH_TYPES': params_with_types,
})
def main(argv):
arg_parser = argparse.ArgumentParser()
build_utils.AddDepfileOption(arg_parser)
arg_parser.add_argument(
'--sources-files',
required=True,
action='append',
help='A list of .sources files which contain Java '
'file paths.')
arg_parser.add_argument(
'--header-path', help='Path to output header file (optional).')
arg_parser.add_argument(
'--srcjar-path',
required=True,
help='Path to output srcjar for GEN_JNI.java (and J/N.java if proxy'
' hash is enabled).')
arg_parser.add_argument('--file-exclusions',
default=[],
help='A list of Java files which should be ignored '
'by the parser.')
arg_parser.add_argument(
'--namespace',
default='',
help='Native namespace to wrap the registration functions '
'into.')
# TODO(crbug.com/898261) hook these flags up to the build config to enable
# mocking in instrumentation tests
arg_parser.add_argument(
'--enable-proxy-mocks',
default=False,
action='store_true',
help='Allows proxy native impls to be mocked through Java.')
arg_parser.add_argument(
'--require-mocks',
default=False,
action='store_true',
help='Requires all used native implementations to have a mock set when '
'called. Otherwise an exception will be thrown.')
arg_parser.add_argument(
'--use-proxy-hash',
action='store_true',
help='Enables hashing of the native declaration for methods in '
'an @JniNatives interface')
arg_parser.add_argument(
'--enable-jni-multiplexing',
action='store_true',
help='Enables JNI multiplexing for Java native methods')
arg_parser.add_argument(
'--manual-jni-registration',
action='store_true',
help='Manually do JNI registration - required for crazy linker')
arg_parser.add_argument('--include-test-only',
action='store_true',
help='Whether to maintain ForTesting JNI methods.')
args = arg_parser.parse_args(build_utils.ExpandFileArgs(argv[1:]))
if not args.enable_proxy_mocks and args.require_mocks:
arg_parser.error(
'Invalid arguments: --require-mocks without --enable-proxy-mocks. '
'Cannot require mocks if they are not enabled.')
if not args.header_path and args.manual_jni_registration:
arg_parser.error(
'Invalid arguments: --manual-jni-registration without --header-path. '
'Cannot manually register JNI if there is no output header file.')
sources_files = sorted(set(build_utils.ParseGnList(args.sources_files)))
java_file_paths = []
for f in sources_files:
# Skip generated files, since the GN targets do not declare any deps. Also
# skip Kotlin files as they are not supported by JNI generation.
java_file_paths.extend(
p for p in build_utils.ReadSourcesList(f) if p.startswith('..')
and p not in args.file_exclusions and not p.endswith('.kt'))
_Generate(args, java_file_paths)
if args.depfile:
build_utils.WriteDepfile(args.depfile, args.srcjar_path,
sources_files + java_file_paths)
if __name__ == '__main__':
sys.exit(main(sys.argv))