package xtc.lang.blink.agent; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import xtc.lang.blink.agent.JNIFunction.ExtraArgumentType; import xtc.lang.blink.agent.JNIFunction.JNIAnnotatedType; import xtc.lang.blink.agent.JNIType.CStringType; import xtc.lang.blink.agent.JNIType.JFieldIDType; import xtc.lang.blink.agent.JNIType.JMethodIDType; import xtc.lang.blink.agent.JNIType.PointerType; import xtc.lang.blink.agent.JNIType.PrimitiveType; import xtc.lang.blink.agent.JNIType.ReferenceType; /** * A JNI Function proxy generator. * * @author Byeong Lee */ public final class GenerateJNIFunctionProxy implements JNIConstants { private static void usage(String reason) { if (reason != null) { System.out.println(reason); } System.out.println( "usage: " + GenerateJNIFunctionProxy.class.getName() + " [options]\n" + "options:" + " -help show help message.\n" + " -o <output file> specify the C source file\n" ); System.exit(-1); } /** * @param args The command line arguments. */ public static void main(String[] args) { String outputSourceFileName = null; for(int i = 0;i < args.length;i++) { if (args[i].equals("-help")) { usage(null); } else if (args[i].equals("-o") ) { if ((i + 1) < args.length) { outputSourceFileName = args[++i]; } else { usage("Please, specify output file after -o"); } } } try { //generate source file. PrintWriter os = new PrintWriter( (outputSourceFileName == null) ? System.out : new FileOutputStream( outputSourceFileName)); GenerateJNIFunctionProxy p = new GenerateJNIFunctionProxy(os); p.gen(); os.flush(); os.close(); } catch (IOException e) { System.err.println("Can not recover from the input or output fault"); } } /** Output stream for the generated JNI function proxy source code. */ private final PrintWriter w; private final ArrayList<ProxyGenerator> pgs; private GenerateJNIFunctionProxy(PrintWriter w) { this.w = w; this.pgs = new ArrayList<ProxyGenerator>(); for(JNIFunction proxyFunction: JNIFunction.jniFunctionList) { String fname = proxyFunction.name; ProxyGenerator pg; if (proxyFunction.extraArgType == ExtraArgumentType.DOT_DOT_DOT) { String targetName = fname + "V"; final JNIFunction targetFunction = JNIFunction.jniFunctionMap.get(targetName); assert targetFunction != null; pg = new VariableArgProxyGenerator(proxyFunction, targetFunction); } else { pg = new FixedArgProxyGenerator(proxyFunction); } pgs.add(pg); } } private void gen() throws IOException{ genHeaders(); genStatDefinitions(); for (final ProxyGenerator pg: pgs) { pg.genFuncDef(w); } genProxyInstall(); genStatReport(); } private void genHeaders() throws IOException{ String[] headers = { "<string.h>", "<assert.h>", "<jni.h>", "<jvmti.h>", "\"agent_main.h\"", "\"state.h\"", "\"common.h\"", "\"agent.h\"", "\"options.h\"", "\"java_method.h\"", "\"jnicheck.h\"", "\"classfile_constants.h\"", }; w.printf("/* This source file is generated by %s.java\n", GenerateJNIFunctionProxy.class.getSimpleName()); w.printf("Please, do not edit manually.*/\n"); for(final String h: headers) { w.printf("#include %s\n", h); } w.printf("\n"); } private void genStatDefinitions() throws IOException { w.printf("static jniNativeInterface* proxy_jni_funcs = NULL;\n"); w.printf("struct bda_c2j_stat_count_jint {\n"); for(final ProxyGenerator p : pgs) { w.printf(" %s %s;\n", "jint", p.getJNIFuncName()); } w.printf("};\n"); w.printf("struct bda_c2j_stat_count_jint bda_c2j_count;\n"); w.printf("struct bda_c2j_stat_count_jint bda_c2j_count_user;\n"); } private void genProxyInstall() throws IOException { w.printf("void bda_c2j_proxy_install(jvmtiEnv *jvmti)\n"); w.printf("{\n"); w.printf(" jvmtiError err;\n"); w.printf(" err = (*jvmti)->GetJNIFunctionTable(jvmti, &proxy_jni_funcs);\n"); w.printf(" assert(err == JVMTI_ERROR_NONE);\n"); for(final ProxyGenerator p : pgs) { w.printf(" proxy_jni_funcs->%s = %s;\n", p.getJNIFuncName(), p.getProxyFuncName()); } w.printf(" err = (*jvmti)->SetJNIFunctionTable(jvmti, proxy_jni_funcs);\n"); w.printf(" assert(err == JVMTI_ERROR_NONE);\n"); w.printf(" memset(&bda_c2j_count, 0, sizeof(bda_c2j_count));\n"); w.printf(" memset(&bda_c2j_count_user, 0, sizeof(bda_c2j_count_user));\n"); w.printf("}\n"); } private void genStatReport() throws IOException { w.print("void bda_c2j_proxy_dump_stat()\n"); w.print("{\n"); w.print(" int sum = 0;\n"); w.print(" int sum_user = 0;\n"); w.print(" int count, count_user;\n"); for(final ProxyGenerator p : pgs) { w.printf("\n"); w.printf(" count = bda_c2j_count.%s;\n", p.getJNIFuncName()); w.printf(" count_user = bda_c2j_count_user.%s;\n", p.getJNIFuncName()); w.printf(" printf(\"%%7u %%7u %%s\\n\", count, count_user, \"%s\");\n", p.getJNIFuncName()); w.printf(" sum += count;\n"); w.printf(" sum_user += count_user;\n"); } w.printf(" printf(\"%%7u %%7u All JNI functions\\n\", sum, sum_user);\n"); w.print("}\n"); } private static class FormalArgument { final JNIAnnotatedType type; final String varName; public FormalArgument(JNIAnnotatedType type, String varName) { this.type = type; this.varName = varName; } } private static abstract class ProxyGenerator { protected final JNIFunction wrapperJNIFunction; protected final FormalArgument[] fargs; protected final JNIFunction targetJNIFunction; ProxyGenerator(JNIFunction proxyFunction, JNIFunction targetFunction) { this.wrapperJNIFunction = proxyFunction; this.targetJNIFunction = targetFunction; this.fargs = new FormalArgument[proxyFunction.argumenTypes.length]; JNIAnnotatedType[] arguments = proxyFunction.argumenTypes; assert arguments.length > 0 && arguments[0].jniType == JNI_ENV; fargs[0] = new FormalArgument(arguments[0], "env"); for(int i = 1;i < arguments.length;i++) { fargs[i] = new FormalArgument(arguments[i], "p" + i); } } String getJNIEnvName() { assert fargs[0].type.jniType == JNI_ENV; return fargs[0].varName; } String getJNIFuncName() { return this.wrapperJNIFunction.name; } String getProxyFuncName() { return "bda_c2j_proxy_" + getJNIFuncName(); } String getStatCountName() { return "bda_c2j_count." + getJNIFuncName(); } String getStatCountUserName() { return "bda_c2j_count_user." + getJNIFuncName(); } FormalArgument findMethodIDVariable() { for(FormalArgument a: fargs) { if (a.type.jniType == JNIConstants.JMETHODID) { return a; } } return null; } private void ensureFormalTypes(JNIType ... types) { for(int i=0; i <types.length;i++) { assert fargs[i].type.jniType == types[i]; } } static final Pattern pGetField = Pattern.compile("(Get|Set)(Static|)(Object|Boolean|Byte|Char|Short|Int|Long|Float|Double)Field"); private void genJNICheckFieldID(PrintWriter w, FormalArgument f, int i) { final String jniFuncName = getJNIFuncName(); final Matcher m = pGetField.matcher(wrapperJNIFunction.name); if (wrapperJNIFunction == JNIFunction.ToReflectedField) { w.printf(" && bda_check_jfieldid_to_reflected_field(%s, %s, %s, %s, \"%s\")\n", "s", fargs[1].varName, fargs[2].varName, fargs[3].varName, jniFuncName); } else if (m.matches()) { boolean getter = m.group(1).equals("Get"); boolean isStatic = m.group(2).equals("Static"); String fieldType = m.group(3); if (!isStatic) { if (getter) { w.printf(" && bda_check_jfieldid_get_instance(s, %s, %s, '%s', \"%s\")\n", fargs[1].varName, fargs[2].varName, getFieldDescFromType(fieldType), jniFuncName); } else { w.printf(" && bda_check_jfieldid_set_instance(s, %s, %s, v, '%s', \"%s\")\n", fargs[1].varName, fargs[2].varName, getFieldDescFromType(fieldType), jniFuncName); } } else { if (getter) { w.printf(" && bda_check_jfieldid_get_static(s, %s, %s, '%s', \"%s\")\n", fargs[1].varName, fargs[2].varName, getFieldDescFromType(fieldType), jniFuncName); } else { w.printf(" && bda_check_jfieldid_set_static(s, %s, %s, v, '%s', \"%s\")\n", fargs[1].varName, fargs[2].varName, getFieldDescFromType(fieldType), jniFuncName); } } } else { assert wrapperJNIFunction.name.equals("ToReflectedField"); } } private static final Pattern jniNewObjectXPattern = Pattern.compile("NewObject(|V|A)"); private static final Pattern jniCallXXXMethodXPattern = Pattern.compile("Call(|Nonvirtual|Static)(Object|Boolean|Byte|Char|Short|Int|Long|Float|Double|Void)Method(|V|A)"); private void genJNICheckMethodID(PrintWriter w, FormalArgument f, int i) { String jniFuncName = getJNIFuncName(); Matcher m; if (wrapperJNIFunction == JNIFunction.ToReflectedMethod) { w.printf(" && bda_check_jmethodid_to_reflected(%s, %s, %s, %s, \"%s\")\n", "s", fargs[1].varName, fargs[2].varName, fargs[3].varName, jniFuncName); } else if ((m = jniNewObjectXPattern.matcher(jniFuncName)).matches()) { String argPass = m.group(1); if (argPass.equals("")) { w.printf(" && bda_check_jmethodid_new_object(%s, %s, %s, awrap, \"%s\")\n", "s", fargs[1].varName, fargs[2].varName, jniFuncName); } else if (argPass.equals("V")) { w.printf(" && bda_check_jmethodid_new_object(%s, %s, %s, %s, \"%s\")\n", "s", fargs[1].varName, fargs[2].varName, "awrap", jniFuncName); } else if (argPass.equals("A")) { w.printf(" && bda_check_jmethodid_new_object(%s, %s, %s, %s, \"%s\")\n", "s", fargs[1].varName, fargs[2].varName, "awrap", jniFuncName); } else { assert false ; } } else if ((m = jniCallXXXMethodXPattern.matcher(jniFuncName)).matches()){ String methodType = m.group(1); String returnType = m.group(2); String argPass = m.group(3); if (argPass.equals("")) { if (methodType.equals("")) { w.printf(" && bda_check_jmethodid_instance(%s, %s, %s, %s, \"%s\", '%s')\n", "s", fargs[1].varName, fargs[2].varName, "awrap", jniFuncName, getFieldDescFromType(returnType)); } else if (methodType.equals("Nonvirtual")) { w.printf(" && bda_check_jmethodid_nonvirtual(%s, %s, %s, %s, %s, \"%s\", '%s')\n", "s", fargs[1].varName, fargs[2].varName, fargs[3].varName, "awrap", jniFuncName, getFieldDescFromType(returnType)); } else if (methodType.equals("Static")) { w.printf(" && bda_check_jmethodid_static(%s, %s, %s, %s, \"%s\", '%s')\n", "s", fargs[1].varName, fargs[2].varName, "awrap", jniFuncName, getFieldDescFromType(returnType)); } else { assert false; } } else if (argPass.equals("V")) { if (methodType.equals("")) { w.printf(" && bda_check_jmethodid_instance(%s, %s, %s, %s, \"%s\", '%s')\n", "s", fargs[1].varName, fargs[2].varName, "awrap", jniFuncName, getFieldDescFromType(returnType)); } else if (methodType.equals("Nonvirtual")) { w.printf(" && bda_check_jmethodid_nonvirtual(%s, %s, %s, %s, %s, \"%s\", '%s')\n", "s", fargs[1].varName, fargs[2].varName, fargs[3].varName, "awrap", jniFuncName, getFieldDescFromType(returnType)); } else if (methodType.equals("Static")) { w.printf(" && bda_check_jmethodid_static(%s, %s, %s, %s, \"%s\", '%s')\n", "s", fargs[1].varName, fargs[2].varName, "awrap", jniFuncName, getFieldDescFromType(returnType)); } else { assert false; } } else if (argPass.equals("A")) { if (methodType.equals("")) { w.printf(" && bda_check_jmethodid_instance(%s, %s, %s, %s, \"%s\", '%s')\n", "s", fargs[1].varName, fargs[2].varName, "awrap", jniFuncName, getFieldDescFromType(returnType)); } else if (methodType.equals("Nonvirtual")) { w.printf(" && bda_check_jmethodid_nonvirtual(%s, %s, %s, %s, %s, \"%s\", '%s')\n", "s", fargs[1].varName, fargs[2].varName, fargs[3].varName, "awrap", jniFuncName, getFieldDescFromType(returnType)); } else if (methodType.equals("Static")) { w.printf(" && bda_check_jmethodid_static(%s, %s, %s, %s, \"%s\", '%s')\n", "s", fargs[1].varName, fargs[2].varName, "awrap", jniFuncName, getFieldDescFromType(returnType)); } else { assert false; } } else { assert false; } } else { assert false : "Not reachable with " + jniFuncName; } } static final String getMethodIDCheckerPrefix(String type) { if (type.equals("")) { return "bda_check_jmethodid_instance_check"; } else if (type.equals("Nonvirtual")) { return "bda_check_jmethodid_nonvirtual_check"; } else if (type.equals("Static")) { return "bda_check_jmethodid_static_check"; } else { assert false; return ""; } } static final HashMap<String,String> type2fieldDesc = new HashMap<String,String>(); { type2fieldDesc.put("Boolean", "Z"); type2fieldDesc.put("Byte", "B"); type2fieldDesc.put("Char", "C"); type2fieldDesc.put("Short", "S"); type2fieldDesc.put("Int", "I"); type2fieldDesc.put("Long", "J"); type2fieldDesc.put("Float", "F"); type2fieldDesc.put("Double", "D"); type2fieldDesc.put("Object", "O"); type2fieldDesc.put("Void", "V"); } static String getFieldDescFromType(String s) { String fdesc = type2fieldDesc.get(s); assert fdesc != null; return fdesc; } static final HashMap<String,String> type2jvalueFieldName = new HashMap<String,String>(); { type2jvalueFieldName.put("Boolean", "z"); type2jvalueFieldName.put("Byte", "b"); type2jvalueFieldName.put("Char", "c"); type2jvalueFieldName.put("Short", "s"); type2jvalueFieldName.put("Int", "i"); type2jvalueFieldName.put("Long", "j"); type2jvalueFieldName.put("Float", "f"); type2jvalueFieldName.put("Double", "d"); type2jvalueFieldName.put("Object", "l"); } static final String getJValueFieldFromType(String type) { String fname = type2jvalueFieldName.get(type); assert fname != null; return fname; } private static String getCformatStringFor(JNIType type) { if (type instanceof PrimitiveType) { PrimitiveType ptype = (PrimitiveType)type; if (ptype == JNIConstants.JBOOLEAN) { return "%d"; } else if (ptype == JNIConstants.JBYTE) { return "%d"; } else if (ptype == JNIConstants.JCHAR) { return "%d"; } else if (ptype == JNIConstants.JSHORT) { return "%d"; } else if (ptype == JNIConstants.JINT) { return "%d"; } else if (ptype == JNIConstants.JLONG) { return "%lld"; } else if (ptype == JNIConstants.JFLOAT) { return "%f"; } else if (ptype == JNIConstants.JDOUBLE) { return "%lf"; } else if (ptype == JNIConstants.JSIZE) { return "%d"; } else if (ptype == JNIConstants.JOBJECT_REF_TYPE) { return "%d"; } else { assert false; return ""; } } else if ((type instanceof ReferenceType) ||(type instanceof JMethodIDType) ||(type instanceof JFieldIDType)) { return "%p"; } else if (type instanceof PointerType) { if (type instanceof CStringType) { return "%s"; } else if (type == JNIConstants.JBYTE_CONST_POINTER) { return "%s"; } else { return "%p"; } } else { assert false; return null; } } static class PrintfFormatAndArgument { final String fmt; final String list; public PrintfFormatAndArgument(String fmt, String list) { this.fmt = fmt; this.list = list; } } PrintfFormatAndArgument getFixedArgumentPrintFormatAndArgumentExpression() { StringBuffer fmt = new StringBuffer(); StringBuffer list = new StringBuffer(); boolean first = true; for(FormalArgument f : fargs) { JNIType jnitype = f.type.jniType; if (first) { first=false;} else { fmt.append(" ");} fmt.append(getCformatStringFor(jnitype)); fmt.append('(').append(jnitype.name).append(')'); list.append(", ").append(f.varName); } return new PrintfFormatAndArgument(fmt.toString(), list.toString()); } void genFuncDef(PrintWriter w) { genFuncBegin(w); genLocalVariableDeclarations(w); genPrologue(w); genGetStateInfo(w); genStatPrologueBegin(w); genJNICheckBefore(w); genC2JInfoBefore(w); genCallOriginal(w); genC2JInfoAfter(w); genJNICheckAfter(w); genFuncEnd(w); } void genFuncBegin(PrintWriter w) { JNIAnnotatedType[] arguments = wrapperJNIFunction.argumenTypes; final String jniFuncName = getJNIFuncName(); final String returnType = wrapperJNIFunction.returnType.getTypeName(); // function declaration w.printf("\n/* proxy for %s*/\n", jniFuncName); w.printf("static %s JNICALL %s(", returnType, getProxyFuncName()); for(int i = 0;i < arguments.length;i++) { w.printf("%s%s %s", i==0? "":", ", fargs[i].type.getTypeName(), fargs[i].varName); } if (wrapperJNIFunction.extraArgType == ExtraArgumentType.DOT_DOT_DOT) { w.printf(", ..."); } w.printf(")\n{\n"); } void genLocalVariableDeclarations(PrintWriter w) { // local variable declaration w.printf(" /* local variables */\n"); if (wrapperJNIFunction.hasReturnType()) { w.printf(" %s result;\n", wrapperJNIFunction.returnType.getTypeName()); } w.printf(" void *fp, *ret_addr, *ret_addr_from_original;\n"); w.printf(" struct bda_c2j_info c2j;\n"); w.printf(" struct bda_state_info *s;\n"); w.printf(" enum bda_mode saved_mode;\n"); w.printf("\n"); } void genPrologue(PrintWriter w) { w.printf(" /* Prologue */\n"); w.printf(" GET_FRAME_POINTER(fp)\n"); w.printf(" GET_RETURN_ADDRESS(ret_addr);\n"); w.printf("\n"); } void genGetStateInfo(PrintWriter w) { w.printf(" /* Obtain a state variable for the current thraed. */\n"); if (wrapperJNIFunction == JNIFunction.ReleaseStringCritical || wrapperJNIFunction == JNIFunction.ReleasePrimitiveArrayCritical) { w.printf(" s = bda_state_find(%s);\n", getJNIEnvName()); } else { w.printf(" s = bda_get_state_info(%s);\n", getJNIEnvName()); } } void genStatPrologueBegin(PrintWriter w) { w.printf(" /* Update call counts. */\n"); w.printf(" if (s != NULL){\n"); w.printf(" %s++;\n", getStatCountName()); w.printf(" if (s->mode == USR_NATIVE) {%s++;}\n", getStatCountUserName()); w.printf(" saved_mode = s->mode;\n"); w.printf(" }\n"); w.printf("\n"); } void genJNICheckBefore(PrintWriter w) { final String jniFuncName = getJNIFuncName(); final String jniEnvName = getJNIEnvName(); boolean exceptionCheck = !wrapperJNIFunction.isExceptionOblivious(); Matcher m; // check the validity of incoming arguments. w.printf(" /* Check the JNI Function call. */\n"); w.printf(" if (agent_options.jniassert && (s != NULL) && (s->mode != JVM) && !bda_is_in_jdwp_region(ret_addr)) {\n"); w.printf(" int success;"); if ((m = jniNewObjectXPattern.matcher(jniFuncName)).matches()) { String argPass = m.group(1); w.printf(" struct bda_var_arg_wrap awrap;\n"); if (argPass.equals("")) { w.printf(" va_start(awrap.value.ap, %s);\n", fargs[fargs.length-1].varName); w.printf(" awrap.type = BDA_VA_LIST;\n"); } else if (argPass.equals("V")) { w.printf(" awrap.type = BDA_VA_LIST;\n"); w.printf(" awrap.value.ap = %s;\n", fargs[3].varName); } else if (argPass.equals("A")) { w.printf(" awrap.type = BDA_JARRAY;\n"); w.printf(" awrap.value.array = %s;\n", fargs[3].varName); } } else if ((m = jniCallXXXMethodXPattern.matcher(jniFuncName)).matches()){ String methodType = m.group(1); String returnType = m.group(2); String argPass = m.group(3); w.printf(" struct bda_var_arg_wrap awrap;\n"); if (argPass.equals("")) { w.printf(" va_start(awrap.value.ap, %s);\n", fargs[fargs.length-1].varName); w.printf(" awrap.type = BDA_VA_LIST;\n"); } else if (argPass.equals("V")) { w.printf(" awrap.type = BDA_VA_LIST;\n"); if (methodType.equals("")) { w.printf(" awrap.value.ap = %s;\n", fargs[3].varName); } else if (methodType.equals("Nonvirtual")) { w.printf(" awrap.value.ap = %s;\n", fargs[4].varName); } else if (methodType.equals("Static")) { w.printf(" awrap.value.ap = %s;\n", fargs[3].varName); } else { assert false; } } else if (argPass.equals("A")) { w.printf(" awrap.type = BDA_JARRAY;\n"); if (methodType.equals("")) { w.printf(" awrap.value.array = %s;\n", fargs[3].varName); } else if (methodType.equals("Nonvirtual")) { w.printf(" awrap.value.array = %s;\n", fargs[4].varName); } else if (methodType.equals("Static")) { w.printf(" awrap.value.array = %s;\n", fargs[3].varName); } else { assert false; } } else { assert false; } } else if ( (m = pGetField.matcher(jniFuncName)).matches()) { boolean getter = m.group(1).equals("Get"); boolean isStatic = m.group(2).equals("Static"); String fieldType = m.group(3); if (!isStatic) { if (!getter) { w.printf(" jvalue v;\n"); w.printf(" v.%s = %s;\n", getJValueFieldFromType(fieldType), fargs[3].varName); } } else { if (!getter) { w.printf(" jvalue v;\n"); w.printf(" v.%s = %s;\n", getJValueFieldFromType(fieldType), fargs[3].varName); } } } w.printf("\n"); // Check JVM state w.printf(" success = 1 \n"); w.printf(" && bda_check_env_match(s, %s, \"%s\")\n", jniEnvName, jniFuncName); if (exceptionCheck) { w.printf(" && bda_check_no_exeception(s, \"%s\")\n", jniFuncName); } if (wrapperJNIFunction == JNIFunction.PopLocalFrame) { w.printf(" && bda_check_local_frame_double_free(s)\n"); } if (!jniFuncName.matches("(Get|Release)(String|PrimitiveArray)Critical")) { w.printf(" && bda_check_no_critical(s, \"%s\")\n", jniFuncName); } // Check parameters for(int i = 1; i < fargs.length;i++) { final FormalArgument f = fargs[i]; final JNIAnnotatedType t = f.type; if (t.nonNull) { w.printf(" && bda_check_non_null(s, %s, %d, \"%s\")\n", f.varName, i, jniFuncName); } } // JNI reference type. if (exceptionCheck) { for(int i = 1; i < fargs.length;i++) { final FormalArgument f = fargs[i]; final JNIAnnotatedType t = f.type; if (t.jniType instanceof ReferenceType){ w.printf(" && bda_check_ref_dangling(s, %s, %d, \"%s\")\n", f.varName, i, jniFuncName); if (t.jniType != JOBJECT) { w.printf(" && bda_check_%s(s, %s, %d, \"%s\")\n", t.jniType.name, f.varName, i, jniFuncName); } } } } else { for(int i = 1; i < fargs.length;i++) { final FormalArgument f = fargs[i]; final JNIAnnotatedType t = f.type; if (t.jniType instanceof ReferenceType){ w.printf(" && (bda_orig_jni_funcs->ExceptionCheck(env) || bda_check_ref_dangling(s, %s, %d, \"%s\"))\n", f.varName, i, jniFuncName); if (t.jniType != JOBJECT) { w.printf(" && (bda_orig_jni_funcs->ExceptionCheck(env) || bda_check_%s(s, %s, %d, \"%s\"))\n", t.jniType.name, f.varName, i, jniFuncName); } } } } if (jniFuncName.equals("DefineClass")) { w.printf(" && bda_check_assignable_jobject_jclass(s, %s, bda_clazz_classloader, %d, \"%s\")\n", fargs[2].varName, 2, jniFuncName); } else if (jniFuncName.equals("FromReflectedMethod")) { w.printf(" && bda_check_jobject_reflected_method(s, %s, %d, \"%s\")\n", fargs[1].varName, 1, jniFuncName); } else if (jniFuncName.equals("FromReflectedField")) { w.printf(" && bda_check_instance_jobject_jclass(s, %s, bda_clazz_field, %d, \"%s\")\n", fargs[1].varName, 1, jniFuncName); } else if (jniFuncName.equals("DeleteGlobalRef")) { w.printf(" && bda_check_jobject_ref_type(s, %s, JNIGlobalRefType, %d, \"%s\")\n", fargs[1].varName, 1, jniFuncName); } else if (jniFuncName.equals("DeleteWeakGlobalRef")) { w.printf(" && bda_check_jobject_ref_type(s, %s, JNIWeakGlobalRefType , %d, \"%s\")\n", fargs[1].varName, 1, jniFuncName); } else if (jniFuncName.equals("DeleteLocalRef")) { w.printf(" && bda_check_jobject_ref_type(s, %s, JNILocalRefType, %d, \"%s\")\n", fargs[1].varName, 1, jniFuncName); } else if (jniFuncName.equals("NewObjectArray")) { w.printf(" && bda_check_assignable_jclass_jobject(s, %s, %s, %d, \"%s\")\n", fargs[2].varName, fargs[3].varName, 2, jniFuncName); } else if (jniFuncName.equals("SetObjectArrayElement")) { w.printf(" && bda_check_assignable_jobjectArray_jobject(s, %s, %s, %d, \"%s\")\n", fargs[1].varName, fargs[3].varName, 3, jniFuncName); } else if (jniFuncName.equals("GetDirectBufferAddress")) { w.printf(" && bda_check_assignable_jobject_jclass(s, %s, bda_clazz_nio_buffer, %d, \"%s\")\n", fargs[1].varName, 1, jniFuncName); } else if (jniFuncName.equals("GetDirectBufferCapacity")) { w.printf(" && bda_check_assignable_jobject_jclass(s, %s, bda_clazz_nio_buffer, %d, \"%s\")\n", fargs[1].varName, 1, jniFuncName); } else if (jniFuncName.equals("ThrowNew")) { w.printf(" && bda_check_assignable_jclass_jclass(s, %s, bda_clazz_throwable, %d, \"%s\")\n", fargs[1].varName, 1, jniFuncName); } else if (jniFuncName.equals("AllocObject")) { w.printf(" && bda_check_jclass_scalar_allocatable(s, %s, %d, \"%s\")\n", fargs[1].varName, 1, jniFuncName); } for(int i = 1; i < fargs.length;i++) { final FormalArgument f = fargs[i]; final JNIAnnotatedType t = f.type; if (t.jniType instanceof JFieldIDType) { genJNICheckFieldID(w, f, i); } else if (t.jniType instanceof JMethodIDType) { genJNICheckMethodID(w, f, i); } } // access if (jniFuncName.matches("Set(Object|Boolean|Byte|Char|Short|Int|Long|Float|Double)Field")) { w.printf(" && bda_check_access_set_instance_field(s, %s, %s, %d, \"%s\")\n", fargs[1].varName, fargs[2].varName, 2, jniFuncName); } else if (jniFuncName.matches("SetStatic(Object|Boolean|Byte|Char|Short|Int|Long|Float|Double)Field")) { w.printf(" && bda_check_access_set_static_field(s, %s, %s, %d, \"%s\")\n", fargs[1].varName, fargs[2].varName, 2, jniFuncName); } // resource release if (jniFuncName.matches("ReleaseString(|UTF)Chars")) { w.printf (" && bda_check_resource_free(s, %s, \"%s\")\n", fargs[2].varName, jniFuncName); } else if (jniFuncName.matches("Release(Boolean|Byte|Char|Short|Int|Long|Float|Double)ArrayElements")) { w.printf (" && bda_check_resource_free(s, %s, \"%s\")\n", fargs[2].varName, jniFuncName); } else if (jniFuncName.matches("ReleaseStringCritical;\n")) { w.printf (" && bda_check_resource_free(s, %s, \"%s\")\n", fargs[2].varName, jniFuncName); } else if (jniFuncName.matches("ReleasePrimitiveArrayCritical")) { w.printf (" && bda_check_resource_free(s, %s, \"%s\")\n", fargs[2].varName, jniFuncName); } w.printf(" ;\n"); // return if any pending exception if (!wrapperJNIFunction.isExceptionOblivious()) { w.printf(" // Just return if an exception is pending here.\n"); w.printf(" if ((agent_options.jniassert )&& bda_orig_jni_funcs->ExceptionCheck(s->env) == JNI_TRUE){\n"); w.printf(" s->mode = saved_mode;\n"); if (wrapperJNIFunction.hasReturnType()) { w.printf(" return 0;\n"); } else { w.printf(" return;\n"); } w.printf(" }\n"); } else { w.printf(" // Just return if an exception is pending here.\n"); w.printf(" if ((agent_options.jniassert)&& !success){\n"); w.printf(" s->mode = saved_mode;\n"); if (wrapperJNIFunction.hasReturnType()) { w.printf(" return 0;\n"); } else { w.printf(" return;\n"); } w.printf(" }\n"); } // prologue action w.printf(" }\n"); w.printf("\n"); } void genC2JInfoBefore(PrintWriter w) { final String proxy_name = getProxyFuncName(); w.printf(" /* Push the c2j_info structure. */\n"); w.printf(" if (s != NULL) {\n"); w.printf("#if defined(__GNUC__)\n"); w.printf(" ret_addr_from_original = &&L_RETURN;\n"); w.printf("#else\n"); w.printf(" ret_addr_from_original = %s;\n", proxy_name); w.printf("#endif\n"); w.printf(" c2j.return_addr = ret_addr_from_original;\n"); w.printf(" c2j.caller_fp = fp;\n"); w.printf(" c2j.jdwp_context = bda_is_in_jdwp_region(ret_addr);\n"); w.printf(" c2j.call_type = %s;\n", wrapperJNIFunction.fclass.name()); switch(wrapperJNIFunction.fclass) { case JNI_CALL_INSTANCE: ensureFormalTypes(JNI_ENV, JOBJECT, JMETHODID); w.printf(" c2j.object = %s;\n",fargs[1].varName); w.printf(" c2j.class = %s;\n", "NULL"); w.printf(" c2j.mid = %s;\n", fargs[2].varName); break; case JNI_CALL_STATIC: ensureFormalTypes(JNI_ENV, JCLASS, JMETHODID); w.printf(" c2j.object = %s;\n", "NULL"); w.printf(" c2j.class = %s;\n", fargs[1].varName); w.printf(" c2j.mid = %s;\n", fargs[2].varName); break; case JNI_CALL_NONVIRTUAL: ensureFormalTypes(JNI_ENV, JOBJECT, JCLASS, JMETHODID); w.printf(" c2j.object = %s;\n", fargs[1].varName); w.printf(" c2j.class = %s;\n", fargs[2].varName); w.printf(" c2j.mid = %s;\n", fargs[3].varName); break; case JNI_CALL_NOT_CLASSIFIED: w.printf(" c2j.object = %s;\n", "NULL"); w.printf(" c2j.class = %s;\n", "NULL"); w.printf(" c2j.mid = %s;\n", "NULL"); break; } w.printf(" bda_state_c2j_call(s, &c2j);\n"); w.printf(" }\n"); w.printf("\n"); } abstract void genCallOriginal(PrintWriter w); void genC2JInfoAfter(PrintWriter w) { w.printf(" /* Pop the c2j_info structure. */\n"); w.printf(" if (s != NULL) {\n"); w.printf(" bda_state_c2j_return(s, &c2j);\n"); w.printf(" s->mode = saved_mode;\n"); w.printf(" }\n"); w.printf("\n"); } void genJNICheckAfter(PrintWriter w) { final String jniFuncName = getJNIFuncName(); w.printf(" /* Check the JNI function return. */\n"); w.printf(" if ((s != NULL) && agent_options.jniassert && (s->mode != JVM)) {\n"); // critical resources if (jniFuncName.matches("Get(String|PrimitiveArray)Critical")) { w.printf(" bda_enter_critical(s, (void*)%s);\n", "result"); } else if (jniFuncName.matches("Release(String|PrimitiveArray)Critical")) { w.printf(" bda_leave_critical(s, (void*)%s);\n", fargs[2].varName); } // Entity-specific typing if (wrapperJNIFunction == JNIFunction.GetMethodID) { w.printf(" if (result != NULL) {\n"); w.printf(" bda_jmethodid_append( result, 0, %s, %s, %s);\n", fargs[1].varName, fargs[2].varName, fargs[3].varName); w.printf(" }\n"); } else if (wrapperJNIFunction == JNIFunction.GetStaticMethodID) { w.printf(" if (result != NULL) {\n"); w.printf(" bda_jmethodid_append(result, 1, %s, %s, %s);\n", fargs[1].varName, fargs[2].varName, fargs[3].varName); w.printf(" }\n"); } else if (wrapperJNIFunction == JNIFunction.GetFieldID) { w.printf(" if (result != NULL) {\n"); w.printf(" bda_jfieldid_append(s, result, %s, 0, %s, %s);\n", fargs[1].varName, fargs[2].varName, fargs[3].varName); w.printf(" }\n"); } else if (wrapperJNIFunction == JNIFunction.GetStaticFieldID) { w.printf(" if (result != NULL) {\n"); w.printf(" bda_jfieldid_append(s, result, %s, 1, %s, %s);\n", fargs[1].varName, fargs[2].varName, fargs[3].varName); w.printf(" }\n"); } // local references if (wrapperJNIFunction.returnLocalReference()) { if (wrapperJNIFunction == JNIFunction.PopLocalFrame) { w.printf(" bda_local_ref_leave(s);\n"); } w.printf(" if (result != NULL) {\n"); w.printf(" if (!bda_check_local_frame_overflow(s, \"%s\")) {\n", jniFuncName); w.printf(" bda_orig_jni_funcs->DeleteLocalRef(env, result);\n"); w.printf(" result = NULL;\n"); w.printf(" } else {\n"); w.printf(" bda_local_ref_add(s, result);"); w.printf(" }\n"); w.printf(" }\n"); } if (wrapperJNIFunction == JNIFunction.PushLocalFrame) { w.printf(" if (result == 0) {\n"); w.printf(" bda_local_ref_enter(s, %s, 0);\n", fargs[1].varName); w.printf(" }\n"); } else if (wrapperJNIFunction == JNIFunction.DeleteLocalRef) { assert !wrapperJNIFunction.hasReturnType(); assert fargs[1].type.jniType == JNIConstants.JOBJECT; w.printf(" if (%s != NULL) {\n", fargs[1].varName); w.printf(" bda_local_ref_delete(s, %s);\n", fargs[1].varName); w.printf(" }\n"); } // global references if (wrapperJNIFunction == JNIFunction.NewGlobalRef) { w.printf(" if (result != NULL) {\n"); w.printf(" bda_global_ref_add(result, 0);\n"); w.printf(" }\n"); } else if (wrapperJNIFunction == JNIFunction.DeleteGlobalRef) { assert fargs[1].type.jniType == JNIConstants.JOBJECT; w.printf(" bda_global_ref_delete(%s, 0);\n", fargs[1].varName); } else if (wrapperJNIFunction == JNIFunction.NewWeakGlobalRef) { w.printf(" if (result != NULL) {\n"); w.printf(" bda_global_ref_add(result, 1);\n"); w.printf(" }\n"); } else if (wrapperJNIFunction == JNIFunction.DeleteWeakGlobalRef) { assert fargs[1].type.jniType == JNIConstants.JWEAK; w.printf(" bda_global_ref_delete(%s, 1);\n", fargs[1].varName); } // VM resources if (jniFuncName.matches("GetString(UTF|)Chars") || jniFuncName.matches("Get(Boolean|Byte|Char|Short|Int|Long|Float|Double)ArrayElements") || jniFuncName.matches("Get(String|PrimitiveArray)Critical")) { w.printf(" if (result != NULL) {bda_resource_acquire(s, result, \"%s\");}\n", jniFuncName); } else if (jniFuncName.matches("ReleaseString(UTF|)Chars") || jniFuncName.matches("Release(String|PrimitiveArray)Critical")) { w.printf(" bda_resource_release(s, %s, \"%s\");\n", fargs[2].varName, jniFuncName); } else if (jniFuncName.matches("Release(Boolean|Byte|Char|Short|Int|Long|Float|Double)ArrayElements")) { w.printf(" if ( %s != JNI_COMMIT) {bda_resource_release(s, %s, \"%s\");}\n", fargs[3].varName, fargs[2].varName, jniFuncName); } // Monitor resources if (wrapperJNIFunction == JNIFunction.MonitorEnter) { w.printf(" if (result == 0) {\n"); w.printf(" bda_monitor_enter(s, %s);\n", fargs[1].varName); w.printf(" }\n"); } else if (wrapperJNIFunction == JNIFunction.MonitorExit) { w.printf(" if (result == 0) {\n"); w.printf(" bda_monitor_exit(s, %s);\n", fargs[1].varName); w.printf(" }\n"); } w.printf(" }\n\n"); } void genFuncEnd(PrintWriter w) { if (wrapperJNIFunction.hasReturnType()) {w.printf(" return result;\n");} w.printf("}\n\n"); } } private static class FixedArgProxyGenerator extends ProxyGenerator { FixedArgProxyGenerator(JNIFunction jniFunction) { super(jniFunction, jniFunction); } void genCallOriginal(PrintWriter w) { w.printf(" /* Call the target JNI function. */\n"); w.printf(" %s bda_orig_jni_funcs->%s(", wrapperJNIFunction.hasReturnType()? "result =":"", getJNIFuncName()); for(int i = 0; i < fargs.length;i++) { FormalArgument f = fargs[i]; w.printf("%s%s", i==0?"":", ", f.varName); } w.printf(");\n"); w.printf(" L_RETURN:\n"); w.printf("\n"); } } private static class VariableArgProxyGenerator extends ProxyGenerator { VariableArgProxyGenerator(JNIFunction proxyFunction, JNIFunction targetFunction) { super(proxyFunction, targetFunction); assert proxyFunction.extraArgType == ExtraArgumentType.DOT_DOT_DOT && targetFunction.extraArgType == ExtraArgumentType.VA_LIST; } void genLocalVariableDeclarations(PrintWriter w) { super.genLocalVariableDeclarations(w); w.printf(" va_list args;\n"); } void genCallOriginal(PrintWriter w) { JNIAnnotatedType[] arguments = wrapperJNIFunction.argumenTypes; String jni_target_fname = targetJNIFunction.name; w.printf(" /* Call the target JNI function. */\n"); w.printf(" va_start(args,p%d);\n", arguments.length-1); if (wrapperJNIFunction.hasReturnType()) { w.printf(" result = "); } w.printf(" bda_orig_jni_funcs->%s(", jni_target_fname); for(int i = 0; i < fargs.length;i++) { FormalArgument f = fargs[i]; w.printf("%s%s", i==0?"":", ", f.varName); } w.printf(", args"); w.printf(");\n"); w.printf(" L_RETURN:\n"); w.printf("\n"); } } }