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

问题描述

这几天在尝试阅读一些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的选项即可完成相似的工作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值