使用 python 正则表达式自动生成 android jni 方法签名

发布于 2025-01-02 20:47:47 字数 2431 浏览 1 评论 0原文

我使用 SWIG 为 Irrlicht C/C++ 3D 引擎生成一些本机 JNI 函数接口,并且我得到了一堆 java 代理类和中间 c/c++ 文件,这些文件实现了 java 本机函数以粘合 java 代理类和 Irrlicht C/ C++ 3D 引擎。

所有java代理类都是在net.sf.jirr包中生成的。生成的java本地方法定义在net.sf.jirr.JirrJNI类中。

由于 SWIG 生成的 c/c++ jni 函数的名称不符合默认的 android jni 函数调用约定,因此我需要手动将这些 java 本机方法注册到相应的 C/C++ jni 函数。

即以生成的java原生方法net.sf.jirr.JirrJNI.SColor_setRed为例: 生成的java本机方法定义为:

public final static native void SColor_setRed(long jarg1, SColor jarg1_, long jarg2);

生成的c/c++ jni函数定义为:

SWIGEXPORT void JNICALL Java_net_sf_jirr_JirrJNI_SColor_1setRed(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2)

由于c/c++ jni函数不符合android c/c++ jni函数调用约定(在本例中生成的c/c++ jni如果需要在 JNI_OnLoad 函数中注册本机函数的情况下调用函数,则函数名称应为 Java_net_sf_jirr_JirrJNI_SColor_setRed。)

因此,我尝试手动注册本机方法就像:

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
    ......
if (!registerNativeMethods(env, JNI_REG_CLASS, (JNINativeMethod*)JNI_METHODS, JNI_METHODS_COUNT)) {
    return -1;
}
/* success -- return valid version number */
result = JNI_VERSION_1_4;

return result;
}

其中 JNI_REG_CLASS、JNI_METHODS、JNI_METHODS_COUNT 定义为:

#define SET_A_JNI_METHOD(name, signature, func_ptr) \
            { (char*)#name, (char*)signature, (void*)func_ptr }
const char* JNI_REG_CLASS = "net/sf/jirr/JirrJNI";
JNINativeMethod JNI_METHODS[] = { SET_A_JNI_METHOD(SColor_setRed, "(JLnet/sf/jirr/SColor;J)V", Java_net_sf_jirr_JirrJNI_SColor_1setRed), ......};
int JNI_METHODS_COUNT = sizeof(JNI_METHODS) / sizeof(JNI_METHODS[0]);

因此,我需要解析 net.sf.jirr.JirrJNI 类并为所有 java 生成一堆 SET_A_JNI_METHOD(......) 宏本机方法。

在这个SET_A_JNI_METHOD宏中,名称部分只是java本机函数名称;签名部分符合java jni方法签名标准(官方java jni JNINativeMethod 参考); func_ptr 部分是添加 Java_$(PackageName)_ 作为前缀,为 java 原生方法名称的每个部分(不包括类名部分)添加一些索引号(这里用“_”分隔)。 即:

Java native method: IFileSystem_getFileDir
name part: IFileSystem_getFileDir; 
func_ptr part: Java_net_sf_jirr_JirrJNI_IFileSystem_1getFileDir; 

Java native method: IFileSystem_addFolderFileArchive__SWIG_0
name part: IFileSystem_addFolderFileArchive__SWIG_0; 
func_ptr part: Java_net_sf_jirr_JirrJNI_IFileSystem_1addFolderFileArchive_1_1SWIG_10

关于如何使用 Python 正则表达式完成此操作有任何提示吗?

I used SWIG for generating some native JNI function interface for Irrlicht C/C++ 3D engine, and I got a bunch of java proxy classes and an intermediate c/c++ files which implements the java native functions to glue the java proxy classes and Irrlicht C/C++ 3d engine.

All java proxy classes are generated within the package net.sf.jirr. And the generated java native methods are defined in the net.sf.jirr.JirrJNI class.

Since the name of the SWIG generated c/c++ jni functions doesn't comply with the default android jni function call convention, I need to register these java native method with the corresponding C/C++ jni function manually.

i.e. Take the generated java native method net.sf.jirr.JirrJNI.SColor_setRed for example:
The generated java native method is defined as:

public final static native void SColor_setRed(long jarg1, SColor jarg1_, long jarg2);

The generated c/c++ jni function is defined as:

SWIGEXPORT void JNICALL Java_net_sf_jirr_JirrJNI_SColor_1setRed(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2)

As the c/c++ jni function doesn't comply with android c/c++ jni function call convention(in this case the generated c/c++ jni function name should be Java_net_sf_jirr_JirrJNI_SColor_setRed if it needs to be called without registering the native function within the JNI_OnLoad function.)

So, I'm trying to register the native methods manually which is like:

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
    ......
if (!registerNativeMethods(env, JNI_REG_CLASS, (JNINativeMethod*)JNI_METHODS, JNI_METHODS_COUNT)) {
    return -1;
}
/* success -- return valid version number */
result = JNI_VERSION_1_4;

return result;
}

where the JNI_REG_CLASS, JNI_METHODS, JNI_METHODS_COUNT are defined as:

#define SET_A_JNI_METHOD(name, signature, func_ptr) \
            { (char*)#name, (char*)signature, (void*)func_ptr }
const char* JNI_REG_CLASS = "net/sf/jirr/JirrJNI";
JNINativeMethod JNI_METHODS[] = { SET_A_JNI_METHOD(SColor_setRed, "(JLnet/sf/jirr/SColor;J)V", Java_net_sf_jirr_JirrJNI_SColor_1setRed), ......};
int JNI_METHODS_COUNT = sizeof(JNI_METHODS) / sizeof(JNI_METHODS[0]);

So, I need to parse the net.sf.jirr.JirrJNI class and generate a bunch of SET_A_JNI_METHOD(......) macroes for all of the java native methods.

In this SET_A_JNI_METHOD macro, the name part is just the java native function name; the signature part complies with the java jni method signature standards(which is explained in the official java jni JNINativeMethod reference); the func_ptr part is to add Java_$(PackageName)_ as the prefix add some index number to each part(excluding the class name part) of the java native method name(separated by "_" here).
i.e:

Java native method: IFileSystem_getFileDir
name part: IFileSystem_getFileDir; 
func_ptr part: Java_net_sf_jirr_JirrJNI_IFileSystem_1getFileDir; 

Java native method: IFileSystem_addFolderFileArchive__SWIG_0
name part: IFileSystem_addFolderFileArchive__SWIG_0; 
func_ptr part: Java_net_sf_jirr_JirrJNI_IFileSystem_1addFolderFileArchive_1_1SWIG_10

Any hint on how to get this done with Python regex?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

走野 2025-01-09 20:47:47

这是这个问题的一些粗略的解决方案。它刚刚起作用了。

#!/usr/bin/env python

#import the necessary modules.
import re #for regular expressions.
import sys #for parsing command line opts.

JNI_CPP_FUNC_PREFIX_INDEX = 1
JNI_METHOD_SIGNATURE_MAP = {
#basic type
'void':'V',
'boolean':'Z',
'int':'I',
'long':'J',
'double':'D',
'float':'F',
'byte':'B',
'char':'C',
'short':'S',
#array type
'int[]':'[I',
'float[]':'[F',
'byte[]':'[B',
'char[]':'[C',
'short[]':'[S',
'double[]':'[D',
'long[]':'[J',
'boolean[]':'[Z',
}

#param type = True; return type = False
def generateJniMethodTypeSignature(type, paramORreturnType = True):
    retParamSignature = None
    if type and type > "":
    try:
        retParamSignature = JNI_METHOD_SIGNATURE_MAP[type]
    except:
        if type == "String" :
            retParamSignature = 'LJava/lang/String'
        else :
            retParamSignature = "Lnet/sf/jirr/" + type
        #if paramORreturnType :
        retParamSignature += ";"

    #print "retParamSignature: "
    #print retParamSignature
    return retParamSignature

#if sys.argv[1]:
#   print "File: %s" % (sys.argv[1])
#   logfile = raw_input("Please input a log file to parse, e.g /var/file: ");
try:
    jniMethodsFilePath = sys.argv[1]
    jniMethodsFile = open(jniMethodsFilePath, "r")

    outputFilePath = sys.argv[2]
    outputFile = open(outputFilePath, "w")

    for eachLine in jniMethodsFile.readlines() :
        eachLine = eachLine.strip()
        #print(eachLine)
    #                                               retType  name space (   param space  )
    regex = re.match('^public\sfinal\sstatic\snative\s(\w+)\s(\w+)(\s)*(\()([^)]*)(\s)*(\))', eachLine)
    if regex:
        #'''
        print "whole match: " + regex.group(0)  #whole match.
        '''
        print "retType: " + regex.group(1)      #retType
        print "name: " + regex.group(2)         #name
        print "left space: "
        print regex.group(3)                    #left space
        print "(: " + regex.group(4)            #(
        print "param: " + regex.group(5)        #param
        print "right space: "
        print regex.group(6)                    #right space
        print "): " + regex.group(7)            #)
        #print eachLine
        '''
        retType = regex.group(1).strip() 
        funcName = regex.group(2).strip()
        param = regex.group(5).strip()
        #java native function name
        command = "SET_A_JNI_METHOD(" + funcName + ", \"("

        #print "param: " + regex.group(5) 
        paramRegex = re.split('\s*,\s*', param)
        if paramRegex:
            for eachParam in paramRegex:
                eachParam = eachParam.strip()
                #print "eachParam: " + eachParam
                eachParamRegex = re.split('\s*', eachParam)
                if eachParamRegex:
                    eachParamType = eachParamRegex[0].strip()
                    #print "eachParamType: " + eachParamType
                    paramTypeSignature = generateJniMethodTypeSignature(eachParamType)
                    if paramTypeSignature:
                        #print "paramTypeSignature: " + paramTypeSignature
                        command = command + paramTypeSignature
            command = command + ")"
            retTypeSignature = generateJniMethodTypeSignature(retType, False)
            if retTypeSignature:
                #parameter type signature.
                command = command + retTypeSignature + "\", "
                #print "command: " + command
            #c/c++ jni function name
            funcNameRegex = re.split('_', funcName)
            if funcNameRegex:
                #print cppFuncNameRegex
                i = 0
                cppFuncName = "Java_net_sf_jirr_JirrJNI_"

                for eachFuncNamePart in funcNameRegex:
                    #print eachFuncNamePart
                    i = i + 1
                    if i == 1:
                        cppFuncName = cppFuncName + eachFuncNamePart
                        if i != len(funcNameRegex):
                            cppFuncName = cppFuncName + '_'
                        continue
                    cppFuncName = cppFuncName + str(JNI_CPP_FUNC_PREFIX_INDEX) + eachFuncNamePart
                    if i != len(funcNameRegex) :
                        cppFuncName = cppFuncName + '_'

                command = command + cppFuncName + "), "
                print "output: " + command + "\n"
                outputFile.write(command + '\n')
    outputFile.close()
    jniMethodsFile.close()          
except IOError, (errno, strerror):
    print "I/O Error(%s) : %s" % (errno, strerror)

Here is some gross solution for this problem. It just worked.

#!/usr/bin/env python

#import the necessary modules.
import re #for regular expressions.
import sys #for parsing command line opts.

JNI_CPP_FUNC_PREFIX_INDEX = 1
JNI_METHOD_SIGNATURE_MAP = {
#basic type
'void':'V',
'boolean':'Z',
'int':'I',
'long':'J',
'double':'D',
'float':'F',
'byte':'B',
'char':'C',
'short':'S',
#array type
'int[]':'[I',
'float[]':'[F',
'byte[]':'[B',
'char[]':'[C',
'short[]':'[S',
'double[]':'[D',
'long[]':'[J',
'boolean[]':'[Z',
}

#param type = True; return type = False
def generateJniMethodTypeSignature(type, paramORreturnType = True):
    retParamSignature = None
    if type and type > "":
    try:
        retParamSignature = JNI_METHOD_SIGNATURE_MAP[type]
    except:
        if type == "String" :
            retParamSignature = 'LJava/lang/String'
        else :
            retParamSignature = "Lnet/sf/jirr/" + type
        #if paramORreturnType :
        retParamSignature += ";"

    #print "retParamSignature: "
    #print retParamSignature
    return retParamSignature

#if sys.argv[1]:
#   print "File: %s" % (sys.argv[1])
#   logfile = raw_input("Please input a log file to parse, e.g /var/file: ");
try:
    jniMethodsFilePath = sys.argv[1]
    jniMethodsFile = open(jniMethodsFilePath, "r")

    outputFilePath = sys.argv[2]
    outputFile = open(outputFilePath, "w")

    for eachLine in jniMethodsFile.readlines() :
        eachLine = eachLine.strip()
        #print(eachLine)
    #                                               retType  name space (   param space  )
    regex = re.match('^public\sfinal\sstatic\snative\s(\w+)\s(\w+)(\s)*(\()([^)]*)(\s)*(\))', eachLine)
    if regex:
        #'''
        print "whole match: " + regex.group(0)  #whole match.
        '''
        print "retType: " + regex.group(1)      #retType
        print "name: " + regex.group(2)         #name
        print "left space: "
        print regex.group(3)                    #left space
        print "(: " + regex.group(4)            #(
        print "param: " + regex.group(5)        #param
        print "right space: "
        print regex.group(6)                    #right space
        print "): " + regex.group(7)            #)
        #print eachLine
        '''
        retType = regex.group(1).strip() 
        funcName = regex.group(2).strip()
        param = regex.group(5).strip()
        #java native function name
        command = "SET_A_JNI_METHOD(" + funcName + ", \"("

        #print "param: " + regex.group(5) 
        paramRegex = re.split('\s*,\s*', param)
        if paramRegex:
            for eachParam in paramRegex:
                eachParam = eachParam.strip()
                #print "eachParam: " + eachParam
                eachParamRegex = re.split('\s*', eachParam)
                if eachParamRegex:
                    eachParamType = eachParamRegex[0].strip()
                    #print "eachParamType: " + eachParamType
                    paramTypeSignature = generateJniMethodTypeSignature(eachParamType)
                    if paramTypeSignature:
                        #print "paramTypeSignature: " + paramTypeSignature
                        command = command + paramTypeSignature
            command = command + ")"
            retTypeSignature = generateJniMethodTypeSignature(retType, False)
            if retTypeSignature:
                #parameter type signature.
                command = command + retTypeSignature + "\", "
                #print "command: " + command
            #c/c++ jni function name
            funcNameRegex = re.split('_', funcName)
            if funcNameRegex:
                #print cppFuncNameRegex
                i = 0
                cppFuncName = "Java_net_sf_jirr_JirrJNI_"

                for eachFuncNamePart in funcNameRegex:
                    #print eachFuncNamePart
                    i = i + 1
                    if i == 1:
                        cppFuncName = cppFuncName + eachFuncNamePart
                        if i != len(funcNameRegex):
                            cppFuncName = cppFuncName + '_'
                        continue
                    cppFuncName = cppFuncName + str(JNI_CPP_FUNC_PREFIX_INDEX) + eachFuncNamePart
                    if i != len(funcNameRegex) :
                        cppFuncName = cppFuncName + '_'

                command = command + cppFuncName + "), "
                print "output: " + command + "\n"
                outputFile.write(command + '\n')
    outputFile.close()
    jniMethodsFile.close()          
except IOError, (errno, strerror):
    print "I/O Error(%s) : %s" % (errno, strerror)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文