Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
michael@0 | 1 | # This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
michael@0 | 4 | |
michael@0 | 5 | import argparse |
michael@0 | 6 | import re |
michael@0 | 7 | |
michael@0 | 8 | STUB_TEMPLATE = ''' |
michael@0 | 9 | typedef %(returnType)s (*%(functionName)s_t)(%(paramTypes)s); |
michael@0 | 10 | static %(functionName)s_t f_%(functionName)s; |
michael@0 | 11 | extern "C" NS_EXPORT %(returnType)s JNICALL |
michael@0 | 12 | %(functionName)s(%(parameterList)s) { |
michael@0 | 13 | if (!f_%(functionName)s) { |
michael@0 | 14 | arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"), |
michael@0 | 15 | "JNI Function called before it was loaded"); |
michael@0 | 16 | return %(returnValue)s; |
michael@0 | 17 | } |
michael@0 | 18 | %(returnKeyword)s f_%(functionName)s(%(arguments)s); |
michael@0 | 19 | } |
michael@0 | 20 | ''' |
michael@0 | 21 | BINDING_TEMPLATE = ' xul_dlsym("%(functionName)s", &f_%(functionName)s);\n' |
michael@0 | 22 | |
michael@0 | 23 | |
michael@0 | 24 | class Generator: |
michael@0 | 25 | """ |
michael@0 | 26 | Class to convert a javah-produced JNI stub file into stubs/bindings files |
michael@0 | 27 | for inclusion into mozglue. |
michael@0 | 28 | """ |
michael@0 | 29 | def __init__(self, outputfile): |
michael@0 | 30 | self.outputfile = outputfile |
michael@0 | 31 | |
michael@0 | 32 | def write(self, guard, stuff): |
michael@0 | 33 | self.outputfile.write('#ifdef %s\n' % guard) |
michael@0 | 34 | self.outputfile.write(stuff) |
michael@0 | 35 | self.outputfile.write('#endif\n\n') |
michael@0 | 36 | |
michael@0 | 37 | def process(self, inputfile): |
michael@0 | 38 | self.outputfile.write('/* WARNING - This file is autogenerated by ' |
michael@0 | 39 | + 'mobile/android/base/jni-generator.py. ' |
michael@0 | 40 | + 'Do not edit manually! */\n') |
michael@0 | 41 | |
michael@0 | 42 | # this matches lines such as: |
michael@0 | 43 | # JNIEXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onResume |
michael@0 | 44 | # and extracts the return type and the function name |
michael@0 | 45 | nameRegex = re.compile('''JNIEXPORT \s+ |
michael@0 | 46 | (?P<returnType>\S+) \s+ |
michael@0 | 47 | JNICALL \s+ |
michael@0 | 48 | (?P<functionName>\S+)''', re.VERBOSE) |
michael@0 | 49 | |
michael@0 | 50 | # this matches lines such as: |
michael@0 | 51 | # (JNIEnv *, jclass); |
michael@0 | 52 | # and extracts everything within the parens; this will be split |
michael@0 | 53 | # on commas to get the argument types. |
michael@0 | 54 | paramsRegex = re.compile('\((.*)\);') |
michael@0 | 55 | |
michael@0 | 56 | for line in inputfile: |
michael@0 | 57 | line = line.strip() |
michael@0 | 58 | |
michael@0 | 59 | match = re.match(nameRegex, line) |
michael@0 | 60 | if match: |
michael@0 | 61 | returnType = match.group('returnType') |
michael@0 | 62 | functionName = match.group('functionName') |
michael@0 | 63 | |
michael@0 | 64 | match = re.match(paramsRegex, line) |
michael@0 | 65 | if match: |
michael@0 | 66 | paramTypes = re.split('\s*,\s*', match.group(1)) |
michael@0 | 67 | paramNames = ['arg%d' % i for i in range(0, len(paramTypes))] |
michael@0 | 68 | if returnType == 'void': |
michael@0 | 69 | returnValue = '' |
michael@0 | 70 | elif returnType in ('jobject', 'jstring'): |
michael@0 | 71 | returnValue = 'nullptr' |
michael@0 | 72 | elif returnType in ('jint', 'jfloat', 'jdouble', 'jlong'): |
michael@0 | 73 | returnValue = '0' |
michael@0 | 74 | elif returnType == 'jboolean': |
michael@0 | 75 | returnValue = 'false' |
michael@0 | 76 | else: |
michael@0 | 77 | raise Exception(('Unsupported JNI return type %s found; ' |
michael@0 | 78 | + 'please update mobile/android/base/' |
michael@0 | 79 | + 'jni-generator.py to handle this case!') |
michael@0 | 80 | % returnType) |
michael@0 | 81 | |
michael@0 | 82 | self.write('JNI_STUBS', STUB_TEMPLATE % { |
michael@0 | 83 | 'returnType': returnType, |
michael@0 | 84 | 'functionName': functionName, |
michael@0 | 85 | 'paramTypes': ', '.join(paramTypes), |
michael@0 | 86 | 'parameterList': ', '.join('%s %s' % param |
michael@0 | 87 | for param in zip(paramTypes, paramNames)), |
michael@0 | 88 | 'arguments': ', '.join(paramNames), |
michael@0 | 89 | 'returnValue': returnValue, |
michael@0 | 90 | 'returnKeyword': 'return' if returnType != 'void' else ''}) |
michael@0 | 91 | self.write('JNI_BINDINGS', BINDING_TEMPLATE % { |
michael@0 | 92 | 'functionName': functionName}) |
michael@0 | 93 | |
michael@0 | 94 | |
michael@0 | 95 | def main(): |
michael@0 | 96 | parser = argparse.ArgumentParser( |
michael@0 | 97 | description='Generate mozglue bindings for JNI functions.') |
michael@0 | 98 | parser.add_argument('inputfile', type=argparse.FileType('r')) |
michael@0 | 99 | parser.add_argument('outputfile', type=argparse.FileType('w')) |
michael@0 | 100 | args = parser.parse_args() |
michael@0 | 101 | gen = Generator(args.outputfile) |
michael@0 | 102 | try: |
michael@0 | 103 | gen.process(args.inputfile) |
michael@0 | 104 | finally: |
michael@0 | 105 | args.outputfile.close() |
michael@0 | 106 | args.inputfile.close() |
michael@0 | 107 | |
michael@0 | 108 | if __name__ == '__main__': |
michael@0 | 109 | main() |