| Index: base/android/jni_generator/jni_generator.py
|
| diff --git a/base/android/jni_generator/jni_generator.py b/base/android/jni_generator/jni_generator.py
|
| deleted file mode 100755
|
| index 74d0117495dd9362a280c7a8d706454fca02fa59..0000000000000000000000000000000000000000
|
| --- a/base/android/jni_generator/jni_generator.py
|
| +++ /dev/null
|
| @@ -1,1560 +0,0 @@
|
| -#!/usr/bin/env python
|
| -# Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| -# Use of this source code is governed by a BSD-style license that can be
|
| -# found in the LICENSE file.
|
| -
|
| -"""Extracts native methods from a Java file and generates the JNI bindings.
|
| -If you change this, please run and update the tests."""
|
| -
|
| -import collections
|
| -import errno
|
| -import optparse
|
| -import os
|
| -import re
|
| -import string
|
| -from string import Template
|
| -import subprocess
|
| -import sys
|
| -import textwrap
|
| -import zipfile
|
| -
|
| -CHROMIUM_SRC = os.path.join(
|
| - os.path.dirname(__file__), os.pardir, os.pardir, os.pardir)
|
| -BUILD_ANDROID_GYP = os.path.join(
|
| - CHROMIUM_SRC, 'build', 'android', 'gyp')
|
| -
|
| -sys.path.append(BUILD_ANDROID_GYP)
|
| -
|
| -from util import build_utils
|
| -
|
| -
|
| -class ParseError(Exception):
|
| - """Exception thrown when we can't parse the input file."""
|
| -
|
| - def __init__(self, description, *context_lines):
|
| - Exception.__init__(self)
|
| - self.description = description
|
| - self.context_lines = context_lines
|
| -
|
| - def __str__(self):
|
| - context = '\n'.join(self.context_lines)
|
| - return '***\nERROR: %s\n\n%s\n***' % (self.description, context)
|
| -
|
| -
|
| -class Param(object):
|
| - """Describes a param for a method, either java or native."""
|
| -
|
| - def __init__(self, **kwargs):
|
| - self.datatype = kwargs['datatype']
|
| - self.name = kwargs['name']
|
| -
|
| -
|
| -class NativeMethod(object):
|
| - """Describes a C/C++ method that is called by Java code"""
|
| -
|
| - def __init__(self, **kwargs):
|
| - self.static = kwargs['static']
|
| - self.java_class_name = kwargs['java_class_name']
|
| - self.return_type = kwargs['return_type']
|
| - self.name = kwargs['name']
|
| - self.params = kwargs['params']
|
| - if self.params:
|
| - assert type(self.params) is list
|
| - assert type(self.params[0]) is Param
|
| - if (self.params and
|
| - self.params[0].datatype == kwargs.get('ptr_type', 'int') and
|
| - self.params[0].name.startswith('native')):
|
| - self.type = 'method'
|
| - self.p0_type = self.params[0].name[len('native'):]
|
| - if kwargs.get('native_class_name'):
|
| - self.p0_type = kwargs['native_class_name']
|
| - else:
|
| - self.type = 'function'
|
| - self.method_id_var_name = kwargs.get('method_id_var_name', None)
|
| -
|
| -
|
| -class CalledByNative(object):
|
| - """Describes a java method exported to c/c++"""
|
| -
|
| - def __init__(self, **kwargs):
|
| - self.system_class = kwargs['system_class']
|
| - self.unchecked = kwargs['unchecked']
|
| - self.static = kwargs['static']
|
| - self.java_class_name = kwargs['java_class_name']
|
| - self.return_type = kwargs['return_type']
|
| - self.name = kwargs['name']
|
| - self.params = kwargs['params']
|
| - self.method_id_var_name = kwargs.get('method_id_var_name', None)
|
| - self.signature = kwargs.get('signature')
|
| - self.is_constructor = kwargs.get('is_constructor', False)
|
| - self.env_call = GetEnvCall(self.is_constructor, self.static,
|
| - self.return_type)
|
| - self.static_cast = GetStaticCastForReturnType(self.return_type)
|
| -
|
| -
|
| -class ConstantField(object):
|
| - def __init__(self, **kwargs):
|
| - self.name = kwargs['name']
|
| - self.value = kwargs['value']
|
| -
|
| -
|
| -def JavaDataTypeToC(java_type):
|
| - """Returns a C datatype for the given java type."""
|
| - java_pod_type_map = {
|
| - 'int': 'jint',
|
| - 'byte': 'jbyte',
|
| - 'char': 'jchar',
|
| - 'short': 'jshort',
|
| - 'boolean': 'jboolean',
|
| - 'long': 'jlong',
|
| - 'double': 'jdouble',
|
| - 'float': 'jfloat',
|
| - }
|
| - java_type_map = {
|
| - 'void': 'void',
|
| - 'String': 'jstring',
|
| - 'java/lang/String': 'jstring',
|
| - 'java/lang/Class': 'jclass',
|
| - }
|
| -
|
| - if java_type in java_pod_type_map:
|
| - return java_pod_type_map[java_type]
|
| - elif java_type in java_type_map:
|
| - return java_type_map[java_type]
|
| - elif java_type.endswith('[]'):
|
| - if java_type[:-2] in java_pod_type_map:
|
| - return java_pod_type_map[java_type[:-2]] + 'Array'
|
| - return 'jobjectArray'
|
| - elif java_type.startswith('Class'):
|
| - # Checking just the start of the name, rather than a direct comparison,
|
| - # in order to handle generics.
|
| - return 'jclass'
|
| - else:
|
| - return 'jobject'
|
| -
|
| -
|
| -def JavaDataTypeToCForCalledByNativeParam(java_type):
|
| - """Returns a C datatype to be when calling from native."""
|
| - if java_type == 'int':
|
| - return 'JniIntWrapper'
|
| - else:
|
| - return JavaDataTypeToC(java_type)
|
| -
|
| -
|
| -def JavaReturnValueToC(java_type):
|
| - """Returns a valid C return value for the given java type."""
|
| - java_pod_type_map = {
|
| - 'int': '0',
|
| - 'byte': '0',
|
| - 'char': '0',
|
| - 'short': '0',
|
| - 'boolean': 'false',
|
| - 'long': '0',
|
| - 'double': '0',
|
| - 'float': '0',
|
| - 'void': ''
|
| - }
|
| - return java_pod_type_map.get(java_type, 'NULL')
|
| -
|
| -
|
| -class JniParams(object):
|
| - _imports = []
|
| - _fully_qualified_class = ''
|
| - _package = ''
|
| - _inner_classes = []
|
| - _remappings = []
|
| - _implicit_imports = []
|
| -
|
| - @staticmethod
|
| - def SetFullyQualifiedClass(fully_qualified_class):
|
| - JniParams._fully_qualified_class = 'L' + fully_qualified_class
|
| - JniParams._package = '/'.join(fully_qualified_class.split('/')[:-1])
|
| -
|
| - @staticmethod
|
| - def AddAdditionalImport(class_name):
|
| - assert class_name.endswith('.class')
|
| - raw_class_name = class_name[:-len('.class')]
|
| - if '.' in raw_class_name:
|
| - raise SyntaxError('%s cannot be used in @JNIAdditionalImport. '
|
| - 'Only import unqualified outer classes.' % class_name)
|
| - new_import = 'L%s/%s' % (JniParams._package, raw_class_name)
|
| - if new_import in JniParams._imports:
|
| - raise SyntaxError('Do not use JNIAdditionalImport on an already '
|
| - 'imported class: %s' % (new_import.replace('/', '.')))
|
| - JniParams._imports += [new_import]
|
| -
|
| - @staticmethod
|
| - def ExtractImportsAndInnerClasses(contents):
|
| - if not JniParams._package:
|
| - raise RuntimeError('SetFullyQualifiedClass must be called before '
|
| - 'ExtractImportsAndInnerClasses')
|
| - contents = contents.replace('\n', '')
|
| - re_import = re.compile(r'import.*?(?P<class>\S*?);')
|
| - for match in re.finditer(re_import, contents):
|
| - JniParams._imports += ['L' + match.group('class').replace('.', '/')]
|
| -
|
| - re_inner = re.compile(r'(class|interface)\s+?(?P<name>\w+?)\W')
|
| - for match in re.finditer(re_inner, contents):
|
| - inner = match.group('name')
|
| - if not JniParams._fully_qualified_class.endswith(inner):
|
| - JniParams._inner_classes += [JniParams._fully_qualified_class + '$' +
|
| - inner]
|
| -
|
| - re_additional_imports = re.compile(
|
| - r'@JNIAdditionalImport\(\s*{?(?P<class_names>.*?)}?\s*\)')
|
| - for match in re.finditer(re_additional_imports, contents):
|
| - for class_name in match.group('class_names').split(','):
|
| - JniParams.AddAdditionalImport(class_name.strip())
|
| -
|
| - @staticmethod
|
| - def ParseJavaPSignature(signature_line):
|
| - prefix = 'Signature: '
|
| - index = signature_line.find(prefix)
|
| - if index == -1:
|
| - prefix = 'descriptor: '
|
| - index = signature_line.index(prefix)
|
| - return '"%s"' % signature_line[index + len(prefix):]
|
| -
|
| - @staticmethod
|
| - def JavaToJni(param):
|
| - """Converts a java param into a JNI signature type."""
|
| - pod_param_map = {
|
| - 'int': 'I',
|
| - 'boolean': 'Z',
|
| - 'char': 'C',
|
| - 'short': 'S',
|
| - 'long': 'J',
|
| - 'double': 'D',
|
| - 'float': 'F',
|
| - 'byte': 'B',
|
| - 'void': 'V',
|
| - }
|
| - object_param_list = [
|
| - 'Ljava/lang/Boolean',
|
| - 'Ljava/lang/Integer',
|
| - 'Ljava/lang/Long',
|
| - 'Ljava/lang/Object',
|
| - 'Ljava/lang/String',
|
| - 'Ljava/lang/Class',
|
| - 'Ljava/lang/CharSequence',
|
| - 'Ljava/lang/Runnable',
|
| - 'Ljava/lang/Throwable',
|
| - ]
|
| -
|
| - prefix = ''
|
| - # Array?
|
| - while param[-2:] == '[]':
|
| - prefix += '['
|
| - param = param[:-2]
|
| - # Generic?
|
| - if '<' in param:
|
| - param = param[:param.index('<')]
|
| - if param in pod_param_map:
|
| - return prefix + pod_param_map[param]
|
| - if '/' in param:
|
| - # Coming from javap, use the fully qualified param directly.
|
| - return prefix + 'L' + JniParams.RemapClassName(param) + ';'
|
| -
|
| - for qualified_name in (object_param_list +
|
| - [JniParams._fully_qualified_class] +
|
| - JniParams._inner_classes):
|
| - if (qualified_name.endswith('/' + param) or
|
| - qualified_name.endswith('$' + param.replace('.', '$')) or
|
| - qualified_name == 'L' + param):
|
| - return prefix + JniParams.RemapClassName(qualified_name) + ';'
|
| -
|
| - # Is it from an import? (e.g. referecing Class from import pkg.Class;
|
| - # note that referencing an inner class Inner from import pkg.Class.Inner
|
| - # is not supported).
|
| - for qualified_name in JniParams._imports:
|
| - if qualified_name.endswith('/' + param):
|
| - # Ensure it's not an inner class.
|
| - components = qualified_name.split('/')
|
| - if len(components) > 2 and components[-2][0].isupper():
|
| - raise SyntaxError('Inner class (%s) can not be imported '
|
| - 'and used by JNI (%s). Please import the outer '
|
| - 'class and use Outer.Inner instead.' %
|
| - (qualified_name, param))
|
| - return prefix + JniParams.RemapClassName(qualified_name) + ';'
|
| -
|
| - # Is it an inner class from an outer class import? (e.g. referencing
|
| - # Class.Inner from import pkg.Class).
|
| - if '.' in param:
|
| - components = param.split('.')
|
| - outer = '/'.join(components[:-1])
|
| - inner = components[-1]
|
| - for qualified_name in JniParams._imports:
|
| - if qualified_name.endswith('/' + outer):
|
| - return (prefix + JniParams.RemapClassName(qualified_name) +
|
| - '$' + inner + ';')
|
| - raise SyntaxError('Inner class (%s) can not be '
|
| - 'used directly by JNI. Please import the outer '
|
| - 'class, probably:\n'
|
| - 'import %s.%s;' %
|
| - (param, JniParams._package.replace('/', '.'),
|
| - outer.replace('/', '.')))
|
| -
|
| - JniParams._CheckImplicitImports(param)
|
| -
|
| - # Type not found, falling back to same package as this class.
|
| - return (prefix + 'L' +
|
| - JniParams.RemapClassName(JniParams._package + '/' + param) + ';')
|
| -
|
| - @staticmethod
|
| - def _CheckImplicitImports(param):
|
| - # Ensure implicit imports, such as java.lang.*, are not being treated
|
| - # as being in the same package.
|
| - if not JniParams._implicit_imports:
|
| - # This file was generated from android.jar and lists
|
| - # all classes that are implicitly imported.
|
| - with file(os.path.join(os.path.dirname(sys.argv[0]),
|
| - 'android_jar.classes'), 'r') as f:
|
| - JniParams._implicit_imports = f.readlines()
|
| - for implicit_import in JniParams._implicit_imports:
|
| - implicit_import = implicit_import.strip().replace('.class', '')
|
| - implicit_import = implicit_import.replace('/', '.')
|
| - if implicit_import.endswith('.' + param):
|
| - raise SyntaxError('Ambiguous class (%s) can not be used directly '
|
| - 'by JNI.\nPlease import it, probably:\n\n'
|
| - 'import %s;' %
|
| - (param, implicit_import))
|
| -
|
| -
|
| - @staticmethod
|
| - def Signature(params, returns, wrap):
|
| - """Returns the JNI signature for the given datatypes."""
|
| - items = ['(']
|
| - items += [JniParams.JavaToJni(param.datatype) for param in params]
|
| - items += [')']
|
| - items += [JniParams.JavaToJni(returns)]
|
| - if wrap:
|
| - return '\n' + '\n'.join(['"' + item + '"' for item in items])
|
| - else:
|
| - return '"' + ''.join(items) + '"'
|
| -
|
| - @staticmethod
|
| - def Parse(params):
|
| - """Parses the params into a list of Param objects."""
|
| - if not params:
|
| - return []
|
| - ret = []
|
| - for p in [p.strip() for p in params.split(',')]:
|
| - items = p.split(' ')
|
| - if 'final' in items:
|
| - items.remove('final')
|
| - param = Param(
|
| - datatype=items[0],
|
| - name=(items[1] if len(items) > 1 else 'p%s' % len(ret)),
|
| - )
|
| - ret += [param]
|
| - return ret
|
| -
|
| - @staticmethod
|
| - def RemapClassName(class_name):
|
| - """Remaps class names using the jarjar mapping table."""
|
| - for old, new in JniParams._remappings:
|
| - if old.endswith('**') and old[:-2] in class_name:
|
| - return class_name.replace(old[:-2], new, 1)
|
| - if '*' not in old and class_name.endswith(old):
|
| - return class_name.replace(old, new, 1)
|
| -
|
| - return class_name
|
| -
|
| - @staticmethod
|
| - def SetJarJarMappings(mappings):
|
| - """Parse jarjar mappings from a string."""
|
| - JniParams._remappings = []
|
| - for line in mappings.splitlines():
|
| - rule = line.split()
|
| - if rule[0] != 'rule':
|
| - continue
|
| - _, src, dest = rule
|
| - src = src.replace('.', '/')
|
| - dest = dest.replace('.', '/')
|
| - if src.endswith('**'):
|
| - src_real_name = src[:-2]
|
| - else:
|
| - assert not '*' in src
|
| - src_real_name = src
|
| -
|
| - if dest.endswith('@0'):
|
| - JniParams._remappings.append((src, dest[:-2] + src_real_name))
|
| - elif dest.endswith('@1'):
|
| - assert '**' in src
|
| - JniParams._remappings.append((src, dest[:-2]))
|
| - else:
|
| - assert not '@' in dest
|
| - JniParams._remappings.append((src, dest))
|
| -
|
| -
|
| -def ExtractJNINamespace(contents):
|
| - re_jni_namespace = re.compile('.*?@JNINamespace\("(.*?)"\)')
|
| - m = re.findall(re_jni_namespace, contents)
|
| - if not m:
|
| - return ''
|
| - return m[0]
|
| -
|
| -
|
| -def ExtractFullyQualifiedJavaClassName(java_file_name, contents):
|
| - re_package = re.compile('.*?package (.*?);')
|
| - matches = re.findall(re_package, contents)
|
| - if not matches:
|
| - raise SyntaxError('Unable to find "package" line in %s' % java_file_name)
|
| - return (matches[0].replace('.', '/') + '/' +
|
| - os.path.splitext(os.path.basename(java_file_name))[0])
|
| -
|
| -
|
| -def ExtractNatives(contents, ptr_type):
|
| - """Returns a list of dict containing information about a native method."""
|
| - contents = contents.replace('\n', '')
|
| - natives = []
|
| - re_native = re.compile(r'(@NativeClassQualifiedName'
|
| - '\(\"(?P<native_class_name>.*?)\"\)\s+)?'
|
| - '(@NativeCall(\(\"(?P<java_class_name>.*?)\"\))\s+)?'
|
| - '(?P<qualifiers>\w+\s\w+|\w+|\s+)\s*native '
|
| - '(?P<return_type>\S*) '
|
| - '(?P<name>native\w+)\((?P<params>.*?)\);')
|
| - for match in re.finditer(re_native, contents):
|
| - native = NativeMethod(
|
| - static='static' in match.group('qualifiers'),
|
| - java_class_name=match.group('java_class_name'),
|
| - native_class_name=match.group('native_class_name'),
|
| - return_type=match.group('return_type'),
|
| - name=match.group('name').replace('native', ''),
|
| - params=JniParams.Parse(match.group('params')),
|
| - ptr_type=ptr_type)
|
| - natives += [native]
|
| - return natives
|
| -
|
| -
|
| -def GetStaticCastForReturnType(return_type):
|
| - type_map = { 'String' : 'jstring',
|
| - 'java/lang/String' : 'jstring',
|
| - 'boolean[]': 'jbooleanArray',
|
| - 'byte[]': 'jbyteArray',
|
| - 'char[]': 'jcharArray',
|
| - 'short[]': 'jshortArray',
|
| - 'int[]': 'jintArray',
|
| - 'long[]': 'jlongArray',
|
| - 'float[]': 'jfloatArray',
|
| - 'double[]': 'jdoubleArray' }
|
| - ret = type_map.get(return_type, None)
|
| - if ret:
|
| - return ret
|
| - if return_type.endswith('[]'):
|
| - return 'jobjectArray'
|
| - return None
|
| -
|
| -
|
| -def GetEnvCall(is_constructor, is_static, return_type):
|
| - """Maps the types availabe via env->Call__Method."""
|
| - if is_constructor:
|
| - return 'NewObject'
|
| - env_call_map = {'boolean': 'Boolean',
|
| - 'byte': 'Byte',
|
| - 'char': 'Char',
|
| - 'short': 'Short',
|
| - 'int': 'Int',
|
| - 'long': 'Long',
|
| - 'float': 'Float',
|
| - 'void': 'Void',
|
| - 'double': 'Double',
|
| - 'Object': 'Object',
|
| - }
|
| - call = env_call_map.get(return_type, 'Object')
|
| - if is_static:
|
| - call = 'Static' + call
|
| - return 'Call' + call + 'Method'
|
| -
|
| -
|
| -def GetMangledParam(datatype):
|
| - """Returns a mangled identifier for the datatype."""
|
| - if len(datatype) <= 2:
|
| - return datatype.replace('[', 'A')
|
| - ret = ''
|
| - for i in range(1, len(datatype)):
|
| - c = datatype[i]
|
| - if c == '[':
|
| - ret += 'A'
|
| - elif c.isupper() or datatype[i - 1] in ['/', 'L']:
|
| - ret += c.upper()
|
| - return ret
|
| -
|
| -
|
| -def GetMangledMethodName(name, params, return_type):
|
| - """Returns a mangled method name for the given signature.
|
| -
|
| - The returned name can be used as a C identifier and will be unique for all
|
| - valid overloads of the same method.
|
| -
|
| - Args:
|
| - name: string.
|
| - params: list of Param.
|
| - return_type: string.
|
| -
|
| - Returns:
|
| - A mangled name.
|
| - """
|
| - mangled_items = []
|
| - for datatype in [return_type] + [x.datatype for x in params]:
|
| - mangled_items += [GetMangledParam(JniParams.JavaToJni(datatype))]
|
| - mangled_name = name + '_'.join(mangled_items)
|
| - assert re.match(r'[0-9a-zA-Z_]+', mangled_name)
|
| - return mangled_name
|
| -
|
| -
|
| -def MangleCalledByNatives(called_by_natives):
|
| - """Mangles all the overloads from the call_by_natives list."""
|
| - method_counts = collections.defaultdict(
|
| - lambda: collections.defaultdict(lambda: 0))
|
| - for called_by_native in called_by_natives:
|
| - java_class_name = called_by_native.java_class_name
|
| - name = called_by_native.name
|
| - method_counts[java_class_name][name] += 1
|
| - for called_by_native in called_by_natives:
|
| - java_class_name = called_by_native.java_class_name
|
| - method_name = called_by_native.name
|
| - method_id_var_name = method_name
|
| - if method_counts[java_class_name][method_name] > 1:
|
| - method_id_var_name = GetMangledMethodName(method_name,
|
| - called_by_native.params,
|
| - called_by_native.return_type)
|
| - called_by_native.method_id_var_name = method_id_var_name
|
| - return called_by_natives
|
| -
|
| -
|
| -# Regex to match the JNI return types that should be included in a
|
| -# ScopedJavaLocalRef.
|
| -RE_SCOPED_JNI_RETURN_TYPES = re.compile('jobject|jclass|jstring|.*Array')
|
| -
|
| -# Regex to match a string like "@CalledByNative public void foo(int bar)".
|
| -RE_CALLED_BY_NATIVE = re.compile(
|
| - '@CalledByNative(?P<Unchecked>(Unchecked)*?)(?:\("(?P<annotation>.*)"\))?'
|
| - '\s+(?P<prefix>[\w ]*?)'
|
| - '\s*(?P<return_type>\S+?)'
|
| - '\s+(?P<name>\w+)'
|
| - '\s*\((?P<params>[^\)]*)\)')
|
| -
|
| -
|
| -def ExtractCalledByNatives(contents):
|
| - """Parses all methods annotated with @CalledByNative.
|
| -
|
| - Args:
|
| - contents: the contents of the java file.
|
| -
|
| - Returns:
|
| - A list of dict with information about the annotated methods.
|
| - TODO(bulach): return a CalledByNative object.
|
| -
|
| - Raises:
|
| - ParseError: if unable to parse.
|
| - """
|
| - called_by_natives = []
|
| - for match in re.finditer(RE_CALLED_BY_NATIVE, contents):
|
| - called_by_natives += [CalledByNative(
|
| - system_class=False,
|
| - unchecked='Unchecked' in match.group('Unchecked'),
|
| - static='static' in match.group('prefix'),
|
| - java_class_name=match.group('annotation') or '',
|
| - return_type=match.group('return_type'),
|
| - name=match.group('name'),
|
| - params=JniParams.Parse(match.group('params')))]
|
| - # Check for any @CalledByNative occurrences that weren't matched.
|
| - unmatched_lines = re.sub(RE_CALLED_BY_NATIVE, '', contents).split('\n')
|
| - for line1, line2 in zip(unmatched_lines, unmatched_lines[1:]):
|
| - if '@CalledByNative' in line1:
|
| - raise ParseError('could not parse @CalledByNative method signature',
|
| - line1, line2)
|
| - return MangleCalledByNatives(called_by_natives)
|
| -
|
| -
|
| -class JNIFromJavaP(object):
|
| - """Uses 'javap' to parse a .class file and generate the JNI header file."""
|
| -
|
| - def __init__(self, contents, options):
|
| - self.contents = contents
|
| - self.namespace = options.namespace
|
| - for line in contents:
|
| - class_name = re.match(
|
| - '.*?(public).*?(class|interface) (?P<class_name>\S+?)( |\Z)',
|
| - line)
|
| - if class_name:
|
| - self.fully_qualified_class = class_name.group('class_name')
|
| - break
|
| - self.fully_qualified_class = self.fully_qualified_class.replace('.', '/')
|
| - # Java 7's javap includes type parameters in output, like HashSet<T>. Strip
|
| - # away the <...> and use the raw class name that Java 6 would've given us.
|
| - self.fully_qualified_class = self.fully_qualified_class.split('<', 1)[0]
|
| - JniParams.SetFullyQualifiedClass(self.fully_qualified_class)
|
| - self.java_class_name = self.fully_qualified_class.split('/')[-1]
|
| - if not self.namespace:
|
| - self.namespace = 'JNI_' + self.java_class_name
|
| - re_method = re.compile('(?P<prefix>.*?)(?P<return_type>\S+?) (?P<name>\w+?)'
|
| - '\((?P<params>.*?)\)')
|
| - self.called_by_natives = []
|
| - for lineno, content in enumerate(contents[2:], 2):
|
| - match = re.match(re_method, content)
|
| - if not match:
|
| - continue
|
| - self.called_by_natives += [CalledByNative(
|
| - system_class=True,
|
| - unchecked=False,
|
| - static='static' in match.group('prefix'),
|
| - java_class_name='',
|
| - return_type=match.group('return_type').replace('.', '/'),
|
| - name=match.group('name'),
|
| - params=JniParams.Parse(match.group('params').replace('.', '/')),
|
| - signature=JniParams.ParseJavaPSignature(contents[lineno + 1]))]
|
| - re_constructor = re.compile('(.*?)public ' +
|
| - self.fully_qualified_class.replace('/', '.') +
|
| - '\((?P<params>.*?)\)')
|
| - for lineno, content in enumerate(contents[2:], 2):
|
| - match = re.match(re_constructor, content)
|
| - if not match:
|
| - continue
|
| - self.called_by_natives += [CalledByNative(
|
| - system_class=True,
|
| - unchecked=False,
|
| - static=False,
|
| - java_class_name='',
|
| - return_type=self.fully_qualified_class,
|
| - name='Constructor',
|
| - params=JniParams.Parse(match.group('params').replace('.', '/')),
|
| - signature=JniParams.ParseJavaPSignature(contents[lineno + 1]),
|
| - is_constructor=True)]
|
| - self.called_by_natives = MangleCalledByNatives(self.called_by_natives)
|
| -
|
| - self.constant_fields = []
|
| - re_constant_field = re.compile('.*?public static final int (?P<name>.*?);')
|
| - re_constant_field_value = re.compile(
|
| - '.*?Constant(Value| value): int (?P<value>(-*[0-9]+)?)')
|
| - for lineno, content in enumerate(contents[2:], 2):
|
| - match = re.match(re_constant_field, content)
|
| - if not match:
|
| - continue
|
| - value = re.match(re_constant_field_value, contents[lineno + 2])
|
| - if not value:
|
| - value = re.match(re_constant_field_value, contents[lineno + 3])
|
| - if value:
|
| - self.constant_fields.append(
|
| - ConstantField(name=match.group('name'),
|
| - value=value.group('value')))
|
| -
|
| - self.inl_header_file_generator = InlHeaderFileGenerator(
|
| - self.namespace, self.fully_qualified_class, [],
|
| - self.called_by_natives, self.constant_fields, options)
|
| -
|
| - def GetContent(self):
|
| - return self.inl_header_file_generator.GetContent()
|
| -
|
| - @staticmethod
|
| - def CreateFromClass(class_file, options):
|
| - class_name = os.path.splitext(os.path.basename(class_file))[0]
|
| - p = subprocess.Popen(args=[options.javap, '-c', '-verbose',
|
| - '-s', class_name],
|
| - cwd=os.path.dirname(class_file),
|
| - stdout=subprocess.PIPE,
|
| - stderr=subprocess.PIPE)
|
| - stdout, _ = p.communicate()
|
| - jni_from_javap = JNIFromJavaP(stdout.split('\n'), options)
|
| - return jni_from_javap
|
| -
|
| -
|
| -class JNIFromJavaSource(object):
|
| - """Uses the given java source file to generate the JNI header file."""
|
| -
|
| - # Match single line comments, multiline comments, character literals, and
|
| - # double-quoted strings.
|
| - _comment_remover_regex = re.compile(
|
| - r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
|
| - re.DOTALL | re.MULTILINE)
|
| -
|
| - def __init__(self, contents, fully_qualified_class, options):
|
| - contents = self._RemoveComments(contents)
|
| - JniParams.SetFullyQualifiedClass(fully_qualified_class)
|
| - JniParams.ExtractImportsAndInnerClasses(contents)
|
| - jni_namespace = ExtractJNINamespace(contents) or options.namespace
|
| - natives = ExtractNatives(contents, options.ptr_type)
|
| - called_by_natives = ExtractCalledByNatives(contents)
|
| - if len(natives) == 0 and len(called_by_natives) == 0:
|
| - raise SyntaxError('Unable to find any JNI methods for %s.' %
|
| - fully_qualified_class)
|
| - inl_header_file_generator = InlHeaderFileGenerator(
|
| - jni_namespace, fully_qualified_class, natives, called_by_natives,
|
| - [], options)
|
| - self.content = inl_header_file_generator.GetContent()
|
| -
|
| - @classmethod
|
| - def _RemoveComments(cls, contents):
|
| - # We need to support both inline and block comments, and we need to handle
|
| - # strings that contain '//' or '/*'.
|
| - # TODO(bulach): This is a bit hacky. It would be cleaner to use a real Java
|
| - # parser. Maybe we could ditch JNIFromJavaSource and just always use
|
| - # JNIFromJavaP; or maybe we could rewrite this script in Java and use APT.
|
| - # http://code.google.com/p/chromium/issues/detail?id=138941
|
| - def replacer(match):
|
| - # Replace matches that are comments with nothing; return literals/strings
|
| - # unchanged.
|
| - s = match.group(0)
|
| - if s.startswith('/'):
|
| - return ''
|
| - else:
|
| - return s
|
| - return cls._comment_remover_regex.sub(replacer, contents)
|
| -
|
| - def GetContent(self):
|
| - return self.content
|
| -
|
| - @staticmethod
|
| - def CreateFromFile(java_file_name, options):
|
| - contents = file(java_file_name).read()
|
| - fully_qualified_class = ExtractFullyQualifiedJavaClassName(java_file_name,
|
| - contents)
|
| - return JNIFromJavaSource(contents, fully_qualified_class, options)
|
| -
|
| -
|
| -class InlHeaderFileGenerator(object):
|
| - """Generates an inline header file for JNI integration."""
|
| -
|
| - def __init__(self, namespace, fully_qualified_class, natives,
|
| - called_by_natives, constant_fields, options):
|
| - self.namespace = namespace
|
| - self.fully_qualified_class = fully_qualified_class
|
| - self.class_name = self.fully_qualified_class.split('/')[-1]
|
| - self.natives = natives
|
| - self.called_by_natives = called_by_natives
|
| - self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI'
|
| - self.constant_fields = constant_fields
|
| - self.options = options
|
| - self.init_native = self.ExtractInitNative(options)
|
| -
|
| - def ExtractInitNative(self, options):
|
| - for native in self.natives:
|
| - if options.jni_init_native_name == 'native' + native.name:
|
| - self.natives.remove(native)
|
| - return native
|
| - return None
|
| -
|
| - def GetContent(self):
|
| - """Returns the content of the JNI binding file."""
|
| - template = Template("""\
|
| -// Copyright 2014 The Chromium Authors. All rights reserved.
|
| -// 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
|
| -// ${SCRIPT_NAME}
|
| -// For
|
| -// ${FULLY_QUALIFIED_CLASS}
|
| -
|
| -#ifndef ${HEADER_GUARD}
|
| -#define ${HEADER_GUARD}
|
| -
|
| -#include <jni.h>
|
| -
|
| -${INCLUDES}
|
| -
|
| -#include "base/android/jni_int_wrapper.h"
|
| -
|
| -// Step 1: forward declarations.
|
| -namespace {
|
| -$CLASS_PATH_DEFINITIONS
|
| -$METHOD_ID_DEFINITIONS
|
| -} // namespace
|
| -
|
| -$OPEN_NAMESPACE
|
| -$FORWARD_DECLARATIONS
|
| -
|
| -$CONSTANT_FIELDS
|
| -
|
| -// Step 2: method stubs.
|
| -$METHOD_STUBS
|
| -
|
| -// Step 3: RegisterNatives.
|
| -$JNI_NATIVE_METHODS
|
| -$REGISTER_NATIVES
|
| -$CLOSE_NAMESPACE
|
| -$JNI_REGISTER_NATIVES
|
| -#endif // ${HEADER_GUARD}
|
| -""")
|
| - values = {
|
| - 'SCRIPT_NAME': self.options.script_name,
|
| - 'FULLY_QUALIFIED_CLASS': self.fully_qualified_class,
|
| - 'CLASS_PATH_DEFINITIONS': self.GetClassPathDefinitionsString(),
|
| - 'METHOD_ID_DEFINITIONS': self.GetMethodIDDefinitionsString(),
|
| - 'FORWARD_DECLARATIONS': self.GetForwardDeclarationsString(),
|
| - 'CONSTANT_FIELDS': self.GetConstantFieldsString(),
|
| - 'METHOD_STUBS': self.GetMethodStubsString(),
|
| - 'OPEN_NAMESPACE': self.GetOpenNamespaceString(),
|
| - 'JNI_NATIVE_METHODS': self.GetJNINativeMethodsString(),
|
| - 'REGISTER_NATIVES': self.GetRegisterNativesString(),
|
| - 'CLOSE_NAMESPACE': self.GetCloseNamespaceString(),
|
| - 'HEADER_GUARD': self.header_guard,
|
| - 'INCLUDES': self.GetIncludesString(),
|
| - 'JNI_REGISTER_NATIVES': self.GetJNIRegisterNativesString()
|
| - }
|
| - return WrapOutput(template.substitute(values))
|
| -
|
| - def GetClassPathDefinitionsString(self):
|
| - ret = []
|
| - ret += [self.GetClassPathDefinitions()]
|
| - return '\n'.join(ret)
|
| -
|
| - def GetMethodIDDefinitionsString(self):
|
| - """Returns the definition of method ids for the called by native methods."""
|
| - if not self.options.eager_called_by_natives:
|
| - return ''
|
| - template = Template("""\
|
| -jmethodID g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = NULL;""")
|
| - ret = []
|
| - for called_by_native in self.called_by_natives:
|
| - values = {
|
| - 'JAVA_CLASS': called_by_native.java_class_name or self.class_name,
|
| - 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
|
| - }
|
| - ret += [template.substitute(values)]
|
| - return '\n'.join(ret)
|
| -
|
| - def GetForwardDeclarationsString(self):
|
| - ret = []
|
| - for native in self.natives:
|
| - if native.type != 'method':
|
| - ret += [self.GetForwardDeclaration(native)]
|
| - if self.options.native_exports and ret:
|
| - return '\nextern "C" {\n' + "\n".join(ret) + '\n}; // extern "C"'
|
| - return '\n'.join(ret)
|
| -
|
| - def GetConstantFieldsString(self):
|
| - if not self.constant_fields:
|
| - return ''
|
| - ret = ['enum Java_%s_constant_fields {' % self.class_name]
|
| - for c in self.constant_fields:
|
| - ret += [' %s = %s,' % (c.name, c.value)]
|
| - ret += ['};']
|
| - return '\n'.join(ret)
|
| -
|
| - def GetMethodStubsString(self):
|
| - """Returns the code corresponding to method stubs."""
|
| - ret = []
|
| - for native in self.natives:
|
| - if native.type == 'method':
|
| - ret += [self.GetNativeMethodStubString(native)]
|
| - if self.options.eager_called_by_natives:
|
| - ret += self.GetEagerCalledByNativeMethodStubs()
|
| - else:
|
| - ret += self.GetLazyCalledByNativeMethodStubs()
|
| -
|
| - if self.options.native_exports and ret:
|
| - return '\nextern "C" {\n' + "\n".join(ret) + '\n}; // extern "C"'
|
| - return '\n'.join(ret)
|
| -
|
| - def GetLazyCalledByNativeMethodStubs(self):
|
| - return [self.GetLazyCalledByNativeMethodStub(called_by_native)
|
| - for called_by_native in self.called_by_natives]
|
| -
|
| - def GetEagerCalledByNativeMethodStubs(self):
|
| - ret = []
|
| - if self.called_by_natives:
|
| - ret += ['namespace {']
|
| - for called_by_native in self.called_by_natives:
|
| - ret += [self.GetEagerCalledByNativeMethodStub(called_by_native)]
|
| - ret += ['} // namespace']
|
| - return ret
|
| -
|
| - def GetIncludesString(self):
|
| - if not self.options.includes:
|
| - return ''
|
| - includes = self.options.includes.split(',')
|
| - return '\n'.join('#include "%s"' % x for x in includes)
|
| -
|
| - def GetKMethodsString(self, clazz):
|
| - ret = []
|
| - for native in self.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 SubstituteNativeMethods(self, template):
|
| - """Substitutes JAVA_CLASS and KMETHODS in the provided template."""
|
| - ret = []
|
| - all_classes = self.GetUniqueClasses(self.natives)
|
| - all_classes[self.class_name] = self.fully_qualified_class
|
| - for clazz in all_classes:
|
| - kmethods = self.GetKMethodsString(clazz)
|
| - if kmethods:
|
| - values = {'JAVA_CLASS': clazz,
|
| - 'KMETHODS': kmethods}
|
| - ret += [template.substitute(values)]
|
| - if not ret: return ''
|
| - return '\n' + '\n'.join(ret)
|
| -
|
| - def GetJNINativeMethodsString(self):
|
| - """Returns the implementation of the array of native methods."""
|
| - if self.options.native_exports and not self.options.native_exports_optional:
|
| - return ''
|
| - template = Template("""\
|
| -static const JNINativeMethod kMethods${JAVA_CLASS}[] = {
|
| -${KMETHODS}
|
| -};
|
| -""")
|
| - return self.SubstituteNativeMethods(template)
|
| -
|
| - def GetRegisterCalledByNativesImplString(self):
|
| - """Returns the code for registering the called by native methods."""
|
| - if not self.options.eager_called_by_natives:
|
| - return ''
|
| - template = Template("""\
|
| - g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = ${GET_METHOD_ID_IMPL}
|
| - if (g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} == NULL) {
|
| - return false;
|
| - }
|
| - """)
|
| - ret = []
|
| - for called_by_native in self.called_by_natives:
|
| - values = {
|
| - 'JAVA_CLASS': called_by_native.java_class_name or self.class_name,
|
| - 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
|
| - 'GET_METHOD_ID_IMPL': self.GetMethodIDImpl(called_by_native),
|
| - }
|
| - ret += [template.substitute(values)]
|
| - return '\n'.join(ret)
|
| -
|
| - def GetRegisterNativesString(self):
|
| - """Returns the code for RegisterNatives."""
|
| - template = Template("""\
|
| -${REGISTER_NATIVES_SIGNATURE} {
|
| -${EARLY_EXIT}
|
| -${CLASSES}
|
| -${NATIVES}
|
| -${CALLED_BY_NATIVES}
|
| - return true;
|
| -}
|
| -""")
|
| - signature = 'static bool RegisterNativesImpl(JNIEnv* env'
|
| - if self.init_native:
|
| - signature += ', jclass clazz)'
|
| - else:
|
| - signature += ')'
|
| -
|
| - early_exit = ''
|
| - if self.options.native_exports_optional:
|
| - early_exit = """\
|
| - if (base::android::IsManualJniRegistrationDisabled()) return true;
|
| -"""
|
| -
|
| - natives = self.GetRegisterNativesImplString()
|
| - called_by_natives = self.GetRegisterCalledByNativesImplString()
|
| - values = {'REGISTER_NATIVES_SIGNATURE': signature,
|
| - 'EARLY_EXIT': early_exit,
|
| - 'CLASSES': self.GetFindClasses(),
|
| - 'NATIVES': natives,
|
| - 'CALLED_BY_NATIVES': called_by_natives,
|
| - }
|
| - return template.substitute(values)
|
| -
|
| - def GetRegisterNativesImplString(self):
|
| - """Returns the shared implementation for RegisterNatives."""
|
| - if self.options.native_exports and not self.options.native_exports_optional:
|
| - return ''
|
| -
|
| - template = Template("""\
|
| - const int kMethods${JAVA_CLASS}Size = arraysize(kMethods${JAVA_CLASS});
|
| -
|
| - if (env->RegisterNatives(${JAVA_CLASS}_clazz(env),
|
| - kMethods${JAVA_CLASS},
|
| - kMethods${JAVA_CLASS}Size) < 0) {
|
| - jni_generator::HandleRegistrationError(
|
| - env, ${JAVA_CLASS}_clazz(env), __FILE__);
|
| - return false;
|
| - }
|
| -""")
|
| - return self.SubstituteNativeMethods(template)
|
| -
|
| - def GetJNIRegisterNativesString(self):
|
| - """Returns the implementation for the JNI registration of native methods."""
|
| - if not self.init_native:
|
| - return ''
|
| -
|
| - template = Template("""\
|
| -extern "C" JNIEXPORT bool JNICALL
|
| -Java_${FULLY_QUALIFIED_CLASS}_${INIT_NATIVE_NAME}(JNIEnv* env, jclass clazz) {
|
| - return ${NAMESPACE}RegisterNativesImpl(env, clazz);
|
| -}
|
| -""")
|
| -
|
| - if self.options.native_exports:
|
| - java_name = JniParams.RemapClassName(self.fully_qualified_class)
|
| - java_name = java_name.replace('_', '_1').replace('/', '_')
|
| - else:
|
| - java_name = self.fully_qualified_class.replace('/', '_')
|
| -
|
| - namespace = ''
|
| - if self.namespace:
|
| - namespace = self.namespace + '::'
|
| - values = {'FULLY_QUALIFIED_CLASS': java_name,
|
| - 'INIT_NATIVE_NAME': 'native' + self.init_native.name,
|
| - 'NAMESPACE': namespace,
|
| - 'REGISTER_NATIVES_IMPL': self.GetRegisterNativesImplString()
|
| - }
|
| - return template.substitute(values)
|
| -
|
| - def GetOpenNamespaceString(self):
|
| - if self.namespace:
|
| - all_namespaces = ['namespace %s {' % ns
|
| - for ns in self.namespace.split('::')]
|
| - return '\n'.join(all_namespaces)
|
| - return ''
|
| -
|
| - def GetCloseNamespaceString(self):
|
| - if self.namespace:
|
| - all_namespaces = ['} // namespace %s' % ns
|
| - for ns in self.namespace.split('::')]
|
| - all_namespaces.reverse()
|
| - return '\n'.join(all_namespaces) + '\n'
|
| - return ''
|
| -
|
| - def GetJNIFirstParam(self, native):
|
| - ret = []
|
| - if native.type == 'method':
|
| - ret = ['jobject jcaller']
|
| - elif native.type == 'function':
|
| - if native.static:
|
| - ret = ['jclass jcaller']
|
| - else:
|
| - ret = ['jobject jcaller']
|
| - return ret
|
| -
|
| - def GetParamsInDeclaration(self, native):
|
| - """Returns the params for the stub declaration.
|
| -
|
| - Args:
|
| - native: the native dictionary describing the method.
|
| -
|
| - Returns:
|
| - A string containing the params.
|
| - """
|
| - return ',\n '.join(self.GetJNIFirstParam(native) +
|
| - [JavaDataTypeToC(param.datatype) + ' ' +
|
| - param.name
|
| - for param in native.params])
|
| -
|
| - def GetCalledByNativeParamsInDeclaration(self, called_by_native):
|
| - return ',\n '.join([
|
| - JavaDataTypeToCForCalledByNativeParam(param.datatype) + ' ' +
|
| - param.name
|
| - for param in called_by_native.params])
|
| -
|
| - def GetStubName(self, native):
|
| - """Return the name of the stub function for this native method.
|
| -
|
| - Args:
|
| - native: the native dictionary describing the method.
|
| -
|
| - Returns:
|
| - A string with the stub function name. For native exports mode this is the
|
| - Java_* symbol name required by the JVM; otherwise it is just the name of
|
| - the native method itself.
|
| - """
|
| - if self.options.native_exports:
|
| - template = Template("Java_${JAVA_NAME}_native${NAME}")
|
| -
|
| - java_name = JniParams.RemapClassName(self.fully_qualified_class)
|
| - java_name = java_name.replace('_', '_1').replace('/', '_')
|
| - if native.java_class_name:
|
| - java_name += '_00024' + native.java_class_name
|
| -
|
| - values = {'NAME': native.name,
|
| - 'JAVA_NAME': java_name}
|
| - return template.substitute(values)
|
| - else:
|
| - return native.name
|
| -
|
| - def GetForwardDeclaration(self, native):
|
| - template_str = """
|
| -static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS});
|
| -"""
|
| - if self.options.native_exports:
|
| - template_str += """
|
| -__attribute__((visibility("default")))
|
| -${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS}) {
|
| - return ${NAME}(${PARAMS_IN_CALL});
|
| -}
|
| -"""
|
| - template = Template(template_str)
|
| - params_in_call = []
|
| - if not self.options.pure_native_methods:
|
| - params_in_call = ['env', 'jcaller']
|
| - params_in_call = ', '.join(params_in_call + [p.name for p in native.params])
|
| -
|
| - values = {'RETURN': JavaDataTypeToC(native.return_type),
|
| - 'NAME': native.name,
|
| - 'PARAMS': self.GetParamsInDeclaration(native),
|
| - 'PARAMS_IN_CALL': params_in_call,
|
| - 'STUB_NAME': self.GetStubName(native)}
|
| - return template.substitute(values)
|
| -
|
| - def GetNativeMethodStubString(self, native):
|
| - """Returns stubs for native methods."""
|
| - if self.options.native_exports:
|
| - template_str = """\
|
| -__attribute__((visibility("default")))
|
| -${RETURN} ${STUB_NAME}(JNIEnv* env,
|
| - ${PARAMS_IN_DECLARATION}) {"""
|
| - else:
|
| - template_str = """\
|
| -static ${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS_IN_DECLARATION}) {"""
|
| - template_str += """
|
| - ${P0_TYPE}* native = reinterpret_cast<${P0_TYPE}*>(${PARAM0_NAME});
|
| - CHECK_NATIVE_PTR(env, jcaller, native, "${NAME}"${OPTIONAL_ERROR_RETURN});
|
| - return native->${NAME}(${PARAMS_IN_CALL})${POST_CALL};
|
| -}
|
| -"""
|
| -
|
| - template = Template(template_str)
|
| - params = []
|
| - if not self.options.pure_native_methods:
|
| - params = ['env', 'jcaller']
|
| - params_in_call = ', '.join(params + [p.name for p in native.params[1:]])
|
| -
|
| - return_type = JavaDataTypeToC(native.return_type)
|
| - optional_error_return = JavaReturnValueToC(native.return_type)
|
| - if optional_error_return:
|
| - optional_error_return = ', ' + optional_error_return
|
| - post_call = ''
|
| - if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type):
|
| - post_call = '.Release()'
|
| -
|
| - values = {
|
| - 'RETURN': return_type,
|
| - 'OPTIONAL_ERROR_RETURN': optional_error_return,
|
| - 'NAME': native.name,
|
| - 'PARAMS_IN_DECLARATION': self.GetParamsInDeclaration(native),
|
| - 'PARAM0_NAME': native.params[0].name,
|
| - 'P0_TYPE': native.p0_type,
|
| - 'PARAMS_IN_CALL': params_in_call,
|
| - 'POST_CALL': post_call,
|
| - 'STUB_NAME': self.GetStubName(native),
|
| - }
|
| - return template.substitute(values)
|
| -
|
| - def GetArgument(self, param):
|
| - return ('as_jint(' + param.name + ')'
|
| - if param.datatype == 'int' else param.name)
|
| -
|
| - def GetArgumentsInCall(self, params):
|
| - """Return a string of arguments to call from native into Java"""
|
| - return [self.GetArgument(p) for p in params]
|
| -
|
| - def GetCalledByNativeValues(self, called_by_native):
|
| - """Fills in necessary values for the CalledByNative methods."""
|
| - java_class = called_by_native.java_class_name or self.class_name
|
| - if called_by_native.static or called_by_native.is_constructor:
|
| - first_param_in_declaration = ''
|
| - first_param_in_call = ('%s_clazz(env)' % java_class)
|
| - else:
|
| - first_param_in_declaration = ', jobject obj'
|
| - first_param_in_call = 'obj'
|
| - params_in_declaration = self.GetCalledByNativeParamsInDeclaration(
|
| - called_by_native)
|
| - if params_in_declaration:
|
| - params_in_declaration = ', ' + params_in_declaration
|
| - params_in_call = ', '.join(self.GetArgumentsInCall(called_by_native.params))
|
| - if params_in_call:
|
| - params_in_call = ', ' + params_in_call
|
| - pre_call = ''
|
| - post_call = ''
|
| - if called_by_native.static_cast:
|
| - pre_call = 'static_cast<%s>(' % called_by_native.static_cast
|
| - post_call = ')'
|
| - check_exception = ''
|
| - if not called_by_native.unchecked:
|
| - check_exception = 'jni_generator::CheckException(env);'
|
| - return_type = JavaDataTypeToC(called_by_native.return_type)
|
| - optional_error_return = JavaReturnValueToC(called_by_native.return_type)
|
| - if optional_error_return:
|
| - optional_error_return = ', ' + optional_error_return
|
| - return_declaration = ''
|
| - return_clause = ''
|
| - if return_type != 'void':
|
| - pre_call = ' ' + pre_call
|
| - return_declaration = return_type + ' ret ='
|
| - if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type):
|
| - return_type = 'base::android::ScopedJavaLocalRef<' + return_type + '>'
|
| - return_clause = 'return ' + return_type + '(env, ret);'
|
| - else:
|
| - return_clause = 'return ret;'
|
| - return {
|
| - 'JAVA_CLASS': java_class,
|
| - 'RETURN_TYPE': return_type,
|
| - 'OPTIONAL_ERROR_RETURN': optional_error_return,
|
| - 'RETURN_DECLARATION': return_declaration,
|
| - 'RETURN_CLAUSE': return_clause,
|
| - 'FIRST_PARAM_IN_DECLARATION': first_param_in_declaration,
|
| - 'PARAMS_IN_DECLARATION': params_in_declaration,
|
| - 'PRE_CALL': pre_call,
|
| - 'POST_CALL': post_call,
|
| - 'ENV_CALL': called_by_native.env_call,
|
| - 'FIRST_PARAM_IN_CALL': first_param_in_call,
|
| - 'PARAMS_IN_CALL': params_in_call,
|
| - 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
|
| - 'CHECK_EXCEPTION': check_exception,
|
| - 'GET_METHOD_ID_IMPL': self.GetMethodIDImpl(called_by_native)
|
| - }
|
| -
|
| - def GetEagerCalledByNativeMethodStub(self, called_by_native):
|
| - """Returns the implementation of the called by native method."""
|
| - template = Template("""
|
| -static ${RETURN_TYPE} ${METHOD_ID_VAR_NAME}(\
|
| -JNIEnv* env${FIRST_PARAM_IN_DECLARATION}${PARAMS_IN_DECLARATION}) {
|
| - ${RETURN_DECLARATION}${PRE_CALL}env->${ENV_CALL}(${FIRST_PARAM_IN_CALL},
|
| - g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}${PARAMS_IN_CALL})${POST_CALL};
|
| - ${RETURN_CLAUSE}
|
| -}""")
|
| - values = self.GetCalledByNativeValues(called_by_native)
|
| - return template.substitute(values)
|
| -
|
| - def GetLazyCalledByNativeMethodStub(self, called_by_native):
|
| - """Returns a string."""
|
| - function_signature_template = Template("""\
|
| -static ${RETURN_TYPE} Java_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}(\
|
| -JNIEnv* env${FIRST_PARAM_IN_DECLARATION}${PARAMS_IN_DECLARATION})""")
|
| - function_header_template = Template("""\
|
| -${FUNCTION_SIGNATURE} {""")
|
| - function_header_with_unused_template = Template("""\
|
| -${FUNCTION_SIGNATURE} __attribute__ ((unused));
|
| -${FUNCTION_SIGNATURE} {""")
|
| - template = Template("""
|
| -static base::subtle::AtomicWord g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = 0;
|
| -${FUNCTION_HEADER}
|
| - /* Must call RegisterNativesImpl() */
|
| - CHECK_CLAZZ(env, ${FIRST_PARAM_IN_CALL},
|
| - ${JAVA_CLASS}_clazz(env)${OPTIONAL_ERROR_RETURN});
|
| - jmethodID method_id =
|
| - ${GET_METHOD_ID_IMPL}
|
| - ${RETURN_DECLARATION}
|
| - ${PRE_CALL}env->${ENV_CALL}(${FIRST_PARAM_IN_CALL},
|
| - method_id${PARAMS_IN_CALL})${POST_CALL};
|
| - ${CHECK_EXCEPTION}
|
| - ${RETURN_CLAUSE}
|
| -}""")
|
| - values = self.GetCalledByNativeValues(called_by_native)
|
| - values['FUNCTION_SIGNATURE'] = (
|
| - function_signature_template.substitute(values))
|
| - if called_by_native.system_class:
|
| - values['FUNCTION_HEADER'] = (
|
| - function_header_with_unused_template.substitute(values))
|
| - else:
|
| - values['FUNCTION_HEADER'] = function_header_template.substitute(values)
|
| - return template.substitute(values)
|
| -
|
| - def GetKMethodArrayEntry(self, native):
|
| - template = Template(' { "native${NAME}", ${JNI_SIGNATURE}, ' +
|
| - 'reinterpret_cast<void*>(${STUB_NAME}) },')
|
| - values = {'NAME': native.name,
|
| - 'JNI_SIGNATURE': JniParams.Signature(native.params,
|
| - native.return_type,
|
| - True),
|
| - 'STUB_NAME': self.GetStubName(native)}
|
| - return template.substitute(values)
|
| -
|
| - def GetUniqueClasses(self, origin):
|
| - ret = {self.class_name: self.fully_qualified_class}
|
| - for entry in origin:
|
| - class_name = self.class_name
|
| - jni_class_path = self.fully_qualified_class
|
| - if entry.java_class_name:
|
| - class_name = entry.java_class_name
|
| - jni_class_path = self.fully_qualified_class + '$' + class_name
|
| - ret[class_name] = jni_class_path
|
| - return ret
|
| -
|
| - def GetClassPathDefinitions(self):
|
| - """Returns the ClassPath constants."""
|
| - ret = []
|
| - template = Template("""\
|
| -const char k${JAVA_CLASS}ClassPath[] = "${JNI_CLASS_PATH}";""")
|
| - native_classes = self.GetUniqueClasses(self.natives)
|
| - called_by_native_classes = self.GetUniqueClasses(self.called_by_natives)
|
| - if self.options.native_exports:
|
| - all_classes = called_by_native_classes
|
| - else:
|
| - all_classes = native_classes
|
| - all_classes.update(called_by_native_classes)
|
| -
|
| - for clazz in all_classes:
|
| - values = {
|
| - 'JAVA_CLASS': clazz,
|
| - 'JNI_CLASS_PATH': JniParams.RemapClassName(all_classes[clazz]),
|
| - }
|
| - ret += [template.substitute(values)]
|
| - ret += ''
|
| -
|
| - class_getter_methods = []
|
| - if self.options.native_exports:
|
| - template = Template("""\
|
| -// Leaking this jclass as we cannot use LazyInstance from some threads.
|
| -base::subtle::AtomicWord g_${JAVA_CLASS}_clazz __attribute__((unused)) = 0;
|
| -#define ${JAVA_CLASS}_clazz(env) \
|
| -base::android::LazyGetClass(env, k${JAVA_CLASS}ClassPath, \
|
| -&g_${JAVA_CLASS}_clazz)""")
|
| - else:
|
| - template = Template("""\
|
| -// Leaking this jclass as we cannot use LazyInstance from some threads.
|
| -jclass g_${JAVA_CLASS}_clazz = NULL;
|
| -#define ${JAVA_CLASS}_clazz(env) g_${JAVA_CLASS}_clazz""")
|
| -
|
| - for clazz in called_by_native_classes:
|
| - values = {
|
| - 'JAVA_CLASS': clazz,
|
| - }
|
| - ret += [template.substitute(values)]
|
| -
|
| - return '\n'.join(ret)
|
| -
|
| - def GetFindClasses(self):
|
| - """Returns the imlementation of FindClass for all known classes."""
|
| - if self.init_native:
|
| - if self.options.native_exports:
|
| - template = Template("""\
|
| - base::subtle::Release_Store(&g_${JAVA_CLASS}_clazz,
|
| - static_cast<base::subtle::AtomicWord>(env->NewWeakGlobalRef(clazz));""")
|
| - else:
|
| - template = Template("""\
|
| - g_${JAVA_CLASS}_clazz = static_cast<jclass>(env->NewWeakGlobalRef(clazz));""")
|
| - else:
|
| - if self.options.native_exports:
|
| - return '\n'
|
| - template = Template("""\
|
| - g_${JAVA_CLASS}_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
|
| - base::android::GetClass(env, k${JAVA_CLASS}ClassPath).obj()));""")
|
| - ret = []
|
| - for clazz in self.GetUniqueClasses(self.called_by_natives):
|
| - values = {'JAVA_CLASS': clazz}
|
| - ret += [template.substitute(values)]
|
| - return '\n'.join(ret)
|
| -
|
| - def GetMethodIDImpl(self, called_by_native):
|
| - """Returns the implementation of GetMethodID."""
|
| - if self.options.eager_called_by_natives:
|
| - template = Template("""\
|
| -env->Get${STATIC_METHOD_PART}MethodID(
|
| - ${JAVA_CLASS}_clazz(env),
|
| - "${JNI_NAME}", ${JNI_SIGNATURE});""")
|
| - else:
|
| - template = Template("""\
|
| - base::android::MethodID::LazyGet<
|
| - base::android::MethodID::TYPE_${STATIC}>(
|
| - env, ${JAVA_CLASS}_clazz(env),
|
| - "${JNI_NAME}",
|
| - ${JNI_SIGNATURE},
|
| - &g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME});
|
| -""")
|
| - jni_name = called_by_native.name
|
| - jni_return_type = called_by_native.return_type
|
| - if called_by_native.is_constructor:
|
| - jni_name = '<init>'
|
| - jni_return_type = 'void'
|
| - if called_by_native.signature:
|
| - signature = called_by_native.signature
|
| - else:
|
| - signature = JniParams.Signature(called_by_native.params,
|
| - jni_return_type,
|
| - True)
|
| - values = {
|
| - 'JAVA_CLASS': called_by_native.java_class_name or self.class_name,
|
| - 'JNI_NAME': jni_name,
|
| - 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
|
| - 'STATIC': 'STATIC' if called_by_native.static else 'INSTANCE',
|
| - 'STATIC_METHOD_PART': 'Static' if called_by_native.static else '',
|
| - 'JNI_SIGNATURE': signature,
|
| - }
|
| - return template.substitute(values)
|
| -
|
| -
|
| -def WrapOutput(output):
|
| - ret = []
|
| - for line in output.splitlines():
|
| - # Do not wrap lines under 80 characters or preprocessor directives.
|
| - if len(line) < 80 or line.lstrip()[:1] == '#':
|
| - stripped = line.rstrip()
|
| - if len(ret) == 0 or len(ret[-1]) or len(stripped):
|
| - ret.append(stripped)
|
| - else:
|
| - first_line_indent = ' ' * (len(line) - len(line.lstrip()))
|
| - subsequent_indent = first_line_indent + ' ' * 4
|
| - if line.startswith('//'):
|
| - subsequent_indent = '//' + subsequent_indent
|
| - wrapper = textwrap.TextWrapper(width=80,
|
| - subsequent_indent=subsequent_indent,
|
| - break_long_words=False)
|
| - ret += [wrapped.rstrip() for wrapped in wrapper.wrap(line)]
|
| - ret += ['']
|
| - return '\n'.join(ret)
|
| -
|
| -
|
| -def ExtractJarInputFile(jar_file, input_file, out_dir):
|
| - """Extracts input file from jar and returns the filename.
|
| -
|
| - The input file is extracted to the same directory that the generated jni
|
| - headers will be placed in. This is passed as an argument to script.
|
| -
|
| - Args:
|
| - jar_file: the jar file containing the input files to extract.
|
| - input_files: the list of files to extract from the jar file.
|
| - out_dir: the name of the directories to extract to.
|
| -
|
| - Returns:
|
| - the name of extracted input file.
|
| - """
|
| - jar_file = zipfile.ZipFile(jar_file)
|
| -
|
| - out_dir = os.path.join(out_dir, os.path.dirname(input_file))
|
| - try:
|
| - os.makedirs(out_dir)
|
| - except OSError as e:
|
| - if e.errno != errno.EEXIST:
|
| - raise
|
| - extracted_file_name = os.path.join(out_dir, os.path.basename(input_file))
|
| - with open(extracted_file_name, 'w') as outfile:
|
| - outfile.write(jar_file.read(input_file))
|
| -
|
| - return extracted_file_name
|
| -
|
| -
|
| -def GenerateJNIHeader(input_file, output_file, options):
|
| - try:
|
| - if os.path.splitext(input_file)[1] == '.class':
|
| - jni_from_javap = JNIFromJavaP.CreateFromClass(input_file, options)
|
| - content = jni_from_javap.GetContent()
|
| - else:
|
| - jni_from_java_source = JNIFromJavaSource.CreateFromFile(
|
| - input_file, options)
|
| - content = jni_from_java_source.GetContent()
|
| - except ParseError, e:
|
| - print e
|
| - sys.exit(1)
|
| - if output_file:
|
| - if not os.path.exists(os.path.dirname(os.path.abspath(output_file))):
|
| - os.makedirs(os.path.dirname(os.path.abspath(output_file)))
|
| - if options.optimize_generation and os.path.exists(output_file):
|
| - with file(output_file, 'r') as f:
|
| - existing_content = f.read()
|
| - if existing_content == content:
|
| - return
|
| - with file(output_file, 'w') as f:
|
| - f.write(content)
|
| - else:
|
| - print content
|
| -
|
| -
|
| -def GetScriptName():
|
| - script_components = os.path.abspath(sys.argv[0]).split(os.path.sep)
|
| - base_index = 0
|
| - for idx, value in enumerate(script_components):
|
| - if value == 'base' or value == 'third_party':
|
| - base_index = idx
|
| - break
|
| - return os.sep.join(script_components[base_index:])
|
| -
|
| -
|
| -def main(argv):
|
| - usage = """usage: %prog [OPTIONS]
|
| -This script will parse the given java source code extracting the native
|
| -declarations and print the header file to stdout (or a file).
|
| -See SampleForTests.java for more details.
|
| - """
|
| - option_parser = optparse.OptionParser(usage=usage)
|
| - build_utils.AddDepfileOption(option_parser)
|
| -
|
| - option_parser.add_option('-j', '--jar_file', dest='jar_file',
|
| - help='Extract the list of input files from'
|
| - ' a specified jar file.'
|
| - ' Uses javap to extract the methods from a'
|
| - ' pre-compiled class. --input should point'
|
| - ' to pre-compiled Java .class files.')
|
| - option_parser.add_option('-n', dest='namespace',
|
| - help='Uses as a namespace in the generated header '
|
| - 'instead of the javap class name, or when there is '
|
| - 'no JNINamespace annotation in the java source.')
|
| - option_parser.add_option('--input_file',
|
| - help='Single input file name. The output file name '
|
| - 'will be derived from it. Must be used with '
|
| - '--output_dir.')
|
| - option_parser.add_option('--output_dir',
|
| - help='The output directory. Must be used with '
|
| - '--input')
|
| - option_parser.add_option('--optimize_generation', type="int",
|
| - default=0, help='Whether we should optimize JNI '
|
| - 'generation by not regenerating files if they have '
|
| - 'not changed.')
|
| - option_parser.add_option('--jarjar',
|
| - help='Path to optional jarjar rules file.')
|
| - option_parser.add_option('--script_name', default=GetScriptName(),
|
| - help='The name of this script in the generated '
|
| - 'header.')
|
| - option_parser.add_option('--includes',
|
| - help='The comma-separated list of header files to '
|
| - 'include in the generated header.')
|
| - option_parser.add_option('--pure_native_methods',
|
| - action='store_true', dest='pure_native_methods',
|
| - help='When true, the native methods will be called '
|
| - 'without any JNI-specific arguments.')
|
| - option_parser.add_option('--ptr_type', default='int',
|
| - type='choice', choices=['int', 'long'],
|
| - help='The type used to represent native pointers in '
|
| - 'Java code. For 32-bit, use int; '
|
| - 'for 64-bit, use long.')
|
| - option_parser.add_option('--jni_init_native_name', default='',
|
| - help='The name of the JNI registration method that '
|
| - 'is used to initialize all native methods. If a '
|
| - 'method with this name is not present in the Java '
|
| - 'source file, setting this option is a no-op. When '
|
| - 'a method with this name is found however, the '
|
| - 'naming convention Java_<packageName>_<className> '
|
| - 'will limit the initialization to only the '
|
| - 'top-level class.')
|
| - option_parser.add_option('--eager_called_by_natives',
|
| - action='store_true', dest='eager_called_by_natives',
|
| - help='When true, the called-by-native methods will '
|
| - 'be initialized in a non-atomic way.')
|
| - option_parser.add_option('--cpp', default='cpp',
|
| - help='The path to cpp command.')
|
| - option_parser.add_option('--javap', default='javap',
|
| - help='The path to javap command.')
|
| - option_parser.add_option('--native_exports', action='store_true',
|
| - help='Native method registration through .so '
|
| - 'exports.')
|
| - option_parser.add_option('--native_exports_optional', action='store_true',
|
| - help='Support both explicit and native method'
|
| - 'registration.')
|
| - options, args = option_parser.parse_args(argv)
|
| - if options.native_exports_optional:
|
| - options.native_exports = True
|
| - if options.jar_file:
|
| - input_file = ExtractJarInputFile(options.jar_file, options.input_file,
|
| - options.output_dir)
|
| - elif options.input_file:
|
| - input_file = options.input_file
|
| - else:
|
| - option_parser.print_help()
|
| - print '\nError: Must specify --jar_file or --input_file.'
|
| - return 1
|
| - output_file = None
|
| - if options.output_dir:
|
| - root_name = os.path.splitext(os.path.basename(input_file))[0]
|
| - output_file = os.path.join(options.output_dir, root_name) + '_jni.h'
|
| - if options.jarjar:
|
| - with open(options.jarjar) as f:
|
| - JniParams.SetJarJarMappings(f.read())
|
| - GenerateJNIHeader(input_file, output_file, options)
|
| -
|
| - if options.depfile:
|
| - build_utils.WriteDepfile(
|
| - options.depfile,
|
| - build_utils.GetPythonDependencies())
|
| -
|
| -
|
| -if __name__ == '__main__':
|
| - sys.exit(main(sys.argv))
|
|
|