michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: import argparse michael@0: import re michael@0: michael@0: STUB_TEMPLATE = ''' michael@0: typedef %(returnType)s (*%(functionName)s_t)(%(paramTypes)s); michael@0: static %(functionName)s_t f_%(functionName)s; michael@0: extern "C" NS_EXPORT %(returnType)s JNICALL michael@0: %(functionName)s(%(parameterList)s) { michael@0: if (!f_%(functionName)s) { michael@0: arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"), michael@0: "JNI Function called before it was loaded"); michael@0: return %(returnValue)s; michael@0: } michael@0: %(returnKeyword)s f_%(functionName)s(%(arguments)s); michael@0: } michael@0: ''' michael@0: BINDING_TEMPLATE = ' xul_dlsym("%(functionName)s", &f_%(functionName)s);\n' michael@0: michael@0: michael@0: class Generator: michael@0: """ michael@0: Class to convert a javah-produced JNI stub file into stubs/bindings files michael@0: for inclusion into mozglue. michael@0: """ michael@0: def __init__(self, outputfile): michael@0: self.outputfile = outputfile michael@0: michael@0: def write(self, guard, stuff): michael@0: self.outputfile.write('#ifdef %s\n' % guard) michael@0: self.outputfile.write(stuff) michael@0: self.outputfile.write('#endif\n\n') michael@0: michael@0: def process(self, inputfile): michael@0: self.outputfile.write('/* WARNING - This file is autogenerated by ' michael@0: + 'mobile/android/base/jni-generator.py. ' michael@0: + 'Do not edit manually! */\n') michael@0: michael@0: # this matches lines such as: michael@0: # JNIEXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onResume michael@0: # and extracts the return type and the function name michael@0: nameRegex = re.compile('''JNIEXPORT \s+ michael@0: (?P\S+) \s+ michael@0: JNICALL \s+ michael@0: (?P\S+)''', re.VERBOSE) michael@0: michael@0: # this matches lines such as: michael@0: # (JNIEnv *, jclass); michael@0: # and extracts everything within the parens; this will be split michael@0: # on commas to get the argument types. michael@0: paramsRegex = re.compile('\((.*)\);') michael@0: michael@0: for line in inputfile: michael@0: line = line.strip() michael@0: michael@0: match = re.match(nameRegex, line) michael@0: if match: michael@0: returnType = match.group('returnType') michael@0: functionName = match.group('functionName') michael@0: michael@0: match = re.match(paramsRegex, line) michael@0: if match: michael@0: paramTypes = re.split('\s*,\s*', match.group(1)) michael@0: paramNames = ['arg%d' % i for i in range(0, len(paramTypes))] michael@0: if returnType == 'void': michael@0: returnValue = '' michael@0: elif returnType in ('jobject', 'jstring'): michael@0: returnValue = 'nullptr' michael@0: elif returnType in ('jint', 'jfloat', 'jdouble', 'jlong'): michael@0: returnValue = '0' michael@0: elif returnType == 'jboolean': michael@0: returnValue = 'false' michael@0: else: michael@0: raise Exception(('Unsupported JNI return type %s found; ' michael@0: + 'please update mobile/android/base/' michael@0: + 'jni-generator.py to handle this case!') michael@0: % returnType) michael@0: michael@0: self.write('JNI_STUBS', STUB_TEMPLATE % { michael@0: 'returnType': returnType, michael@0: 'functionName': functionName, michael@0: 'paramTypes': ', '.join(paramTypes), michael@0: 'parameterList': ', '.join('%s %s' % param michael@0: for param in zip(paramTypes, paramNames)), michael@0: 'arguments': ', '.join(paramNames), michael@0: 'returnValue': returnValue, michael@0: 'returnKeyword': 'return' if returnType != 'void' else ''}) michael@0: self.write('JNI_BINDINGS', BINDING_TEMPLATE % { michael@0: 'functionName': functionName}) michael@0: michael@0: michael@0: def main(): michael@0: parser = argparse.ArgumentParser( michael@0: description='Generate mozglue bindings for JNI functions.') michael@0: parser.add_argument('inputfile', type=argparse.FileType('r')) michael@0: parser.add_argument('outputfile', type=argparse.FileType('w')) michael@0: args = parser.parse_args() michael@0: gen = Generator(args.outputfile) michael@0: try: michael@0: gen.process(args.inputfile) michael@0: finally: michael@0: args.outputfile.close() michael@0: args.inputfile.close() michael@0: michael@0: if __name__ == '__main__': michael@0: main()