Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / base / android / jni_generator / jni_generator.py
index 2733583..32d09f1 100755 (executable)
@@ -124,6 +124,14 @@ def JavaDataTypeToC(java_type):
     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 = {
@@ -146,6 +154,7 @@ class JniParams(object):
   _package = ''
   _inner_classes = []
   _remappings = []
+  _implicit_imports = []
 
   @staticmethod
   def SetFullyQualifiedClass(fully_qualified_class):
@@ -193,6 +202,7 @@ class JniParams(object):
         'Ljava/lang/String',
         'Ljava/lang/Class',
     ]
+
     prefix = ''
     # Array?
     while param[-2:] == '[]':
@@ -246,11 +256,33 @@ class JniParams(object):
                         (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 = ['(']
@@ -580,8 +612,14 @@ class JNIFromJavaP(object):
 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, options)
+    contents = self._RemoveComments(contents)
     JniParams.SetFullyQualifiedClass(fully_qualified_class)
     JniParams.ExtractImportsAndInnerClasses(contents)
     jni_namespace = ExtractJNINamespace(contents) or options.namespace
@@ -595,23 +633,23 @@ class JNIFromJavaSource(object):
         [], options)
     self.content = inl_header_file_generator.GetContent()
 
-  def _RemoveComments(self, contents, options):
+  @classmethod
+  def _RemoveComments(cls, contents):
     # We need to support both inline and block comments, and we need to handle
-    # strings that contain '//' or '/*'. Rather than trying to do all that with
-    # regexps, we just pipe the contents through the C preprocessor. We tell cpp
-    # the file has already been preprocessed, so it just removes comments and
-    # doesn't try to parse #include, #pragma etc.
-    #
-    # TODO(husky): This is a bit hacky. It would be cleaner to use a real Java
+    # 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
-    p = subprocess.Popen(args=[options.cpp, '-fpreprocessed'],
-                         stdin=subprocess.PIPE,
-                         stdout=subprocess.PIPE,
-                         stderr=subprocess.PIPE)
-    stdout, _ = p.communicate(contents)
-    return stdout
+    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
@@ -666,6 +704,8 @@ class InlHeaderFileGenerator(object):
 
 ${INCLUDES}
 
+#include "base/android/jni_int_wrapper.h"
+
 // Step 1: forward declarations.
 namespace {
 $CLASS_PATH_DEFINITIONS
@@ -926,9 +966,10 @@ Java_${FULLY_QUALIFIED_CLASS}_${INIT_NATIVE_NAME}(JNIEnv* env, jclass clazz) {
                            for param in native.params])
 
   def GetCalledByNativeParamsInDeclaration(self, called_by_native):
-    return ',\n    '.join([JavaDataTypeToC(param.datatype) + ' ' +
-                           param.name
-                           for param in called_by_native.params])
+    return ',\n    '.join([
+        JavaDataTypeToCForCalledByNativeParam(param.datatype) + ' ' +
+        param.name
+        for param in called_by_native.params])
 
   def GetForwardDeclaration(self, native):
     template = Template("""
@@ -972,6 +1013,14 @@ static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS_IN_DECLARATION}) {
     }
     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."""
     if called_by_native.static or called_by_native.is_constructor:
@@ -986,7 +1035,7 @@ static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS_IN_DECLARATION}) {
         called_by_native)
     if params_in_declaration:
       params_in_declaration = ', ' + params_in_declaration
-    params_in_call = ', '.join(param.name for param in called_by_native.params)
+    params_in_call = ', '.join(self.GetArgumentsInCall(called_by_native.params))
     if params_in_call:
       params_in_call = ', ' + params_in_call
     pre_call = ''