您的位置:首页 > 其它

输出编译器预处理器的中间文件

2015-10-04 11:55 344 查看

问题描述

这几天在尝试阅读一些OpenJDK的源代码,但是发现Source Insight工具无法跳转到某些些函数,类型的定义上去。例如:

JNI_ENTRY(void, jni_SetStaticObjectField(JNIEnv *env, jclass clazz, jfieldID fieldID, jobject value))
JNIWrapper("SetStaticObjectField");

JNIid* id = jfieldIDWorkaround::from_static_jfieldID(fieldID);
if (JvmtiExport::should_post_field_modification()) {
jvalue field_value;
field_value.l = value;
JvmtiExport::jni_SetField_probe(thread, NULL, NULL, id->holder(), fieldID, true, 'L', (jvalue *)&field_value);
}
id->holder()->java_mirror()->obj_field_put(id->offset(), JNIHandles::resolve(value));

JNI_END
我们知道,这种宏定义的代码导致了阅读上的困难。简单查看一下,是下面的宏带来的问题:

#define JNI_ENTRY(result_type, header)                                                                    \
JNI_ENTRY_NO_PRESERVE(result_type, header)                                                            \
WeakPreserveExceptionMark __wem(thread);

#define JNI_ENTRY_NO_PRESERVE(result_type, header)                                                        \
extern "C" {                                                                                              \
result_type JNICALL header {                                                                            \
JavaThread* thread=JavaThread::thread_from_jni_environment(env);                                      \
assert( !VerifyJNIEnvThread || (thread == Thread::current()), "JNIEnv is only valid in same thread"); \
ThreadInVMfromNative __tiv(thread);                                                                   \
debug_only(VMNativeEntryWrapper __vew;)                                                               \
VM_ENTRY_BASE(result_type, header, thread)

#define JNI_END } }
我需要输出宏展开后的中间代码,才能流畅的阅读这种代码。

解决方法

我们都知道,现代编译器都支持输出预处理器的中间结果,而预处理过程不涉及程序的编译和链接,因而与平台无关。本文以Visual Studio 2012为例来说明如何将OpenJDK的jni.cpp文件预处理生成中间结果文件的。

步骤1:

在\jdk8u\hotspot\src\share\vm\prims\jni.cpp相同的目录下创建一个Makefile文件,内容如下:

CFLAGS_JDKEXE = -DCC_INTERP  -D_GNU_SOURCE -D_REENTRANT -D_LARGEFILE64_SOURCE -D_LP64=1 -D_LITTLE_ENDIAN -DLINUX  -IG:\OpenJDK8\jdk8u\build\linux-x86_64-normal-server-release\jdk\include       -IG:\OpenJDK8\jdk8u\build\linux-x86_64-normal-server-release\jdk\include\linux       -IG:\OpenJDK8\jdk8u\jdk\src\share\javavm\export       -IG:\OpenJDK8\jdk8u\jdk\src\solaris\javavm\export       -IG:\OpenJDK8\jdk8u\jdk\src\share\native\common -IG:\OpenJDK8\jdk8u\jdk\src\solaris\native\common -IG:\OpenJDK8\jdk8u\hotspot\src\share\vm\runtime -IG:\OpenJDK8\jdk8u\hotspot\src\share\vm\precompiled -IG:\OpenJDK8\jdk8u\hotspot\src\share\vm -IG:\OpenJDK8\jdk8u\build\linux-x86_64-normal-server-release\hotspot\linux_amd64_compiler2\generated\

preprocessor:
cl -C -EP $(CFLAGS_JDKEXE) jni.cpp -P
使用Makefile的好处是:在命令行输入简单nmake命令,就可以自动的执行这个Makefile定义的预处理动作。

步骤2:

如图所示,进入Visual Studio2012命令行,注意是“Developer CommandPrompt for VS2012”启动的命令行,而不是"cmd"启动的命令行;并且在Windows使用的命令是nmake而不是Linux上的make。



期间可能有编译错误(例如,需要用“-I”来添加包含路径),需要自行解决编译错误。

生成的中间文件是jni.i文件,用文本编辑工具打开这个".i"文件就可以发现下面的函数了:

extern "C" {
void  jni_SetStaticObjectField(JNIEnv *env, jclass clazz, jfieldID fieldID, jobject value)
{
....
}
}
这样,Source Insight就能够正确jni_SetStaticObjectField函数了。另外,“.i”文件通常展开了很多"#include"指令包含的文件,很容易把这些展开的内容恢复成原来的"#include"语句,进而可以考虑用这个".i"文件来替换原来的".cpp"文件,方便代码阅读。

一点补充

关于预编译器的命令选项,请用“cl /?”帮助来查看。值得注意的是: "-EP"和"-P"都是必需的:"-EP"用于删除“#line 1...”输出; "-P"用于自动输出到“.i”文件。

Linux上应该也有相似的编译选项,参考GCC的选项即可完成相似的工作。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: