IronPython 源码剖析系列(1):IronPython 编译器
自 IronPython 正式发布以来,由于对 Python 语言的喜爱所驱使,同时我想藉此去了解一下编程语言的编译器,分析器等程序是什么原理,如何运作的,所以我开始了对 IronPython 源代码的学习过程。但代码也看了有一段时间了,之前是看一些实现细节,结果越看越糊涂。现在我发现需要改变一下策略了,因为我们了解一个系统总是从对它的使用方法去开始了解,如果直接去了解底层的运作原理,则可能会迷失在代码海洋里面。所以我也准备采取自顶而下的分析方法,捡软柿子捏,从简单的,宏观的入手。至于具体的实现细节,可以慢慢再深入研究。
OK,今天被我抓到的看起来像软柿子的是:
IronPython/Hosting/PythonCompiler.cs
首先我们抓住要点,看这个文件中的主要类: PythonCompiler (Python 编译器)
在这个类中,首先有一堆的属性不用去管,表示的无非是编译器将要输出的文件的类型啊(DLL 还是 EXE),输出路径,引用了哪些程序集啊等等东西。
直奔主题,我们看到 Compile() 方法,这是负责编译的主控制方法。
这个方法不难理解,我读了一遍,注释如下:
/// 编译
/// </summary>
public void Compile() {
string fullPath = Path.GetFullPath(outputAssembly);
string outDir = Path.GetDirectoryName(fullPath);
string fileName = Path.GetFileName(outputAssembly);
// Python 编译器的接受池
PythonCompilerSink sink = new PythonCompilerSink(compilerSink);
// 程序集产生器
assemblyGen = new AssemblyGen(
Path.GetFileNameWithoutExtension(outputAssembly),
outDir, fileName, includeDebugInformation, staticTypes, executable, machine
);
// 是否以设定入口点(entry point)
bool entryPointSet = false;
// 设定默认的主文件(对非 DLL 的输出文件类型而言)
if (mainFile == null && sourceFiles.Count == 1 && targetKind != PEFileKinds.Dll) {
mainFile = sourceFiles[0];
}
// 对每个源文件依次编译
foreach (string sourceFile in sourceFiles) {
// 是否产生 Main 方法
bool createMainMethod = sourceFile == mainFile;
// 每个源代码文件编译为一个模块
CompilePythonModule(sourceFile, sink, createMainMethod);
if (sink.Errors > 0) return;
if (createMainMethod) {
entryPointSet = true;
}
}
// 依次将所有资源文件添加到程序集中
if (resourceFiles != null) {
foreach (ResourceFile rf in resourceFiles) {
assemblyGen.AddResourceFile(rf.Name, rf.File, rf.PublicResource ? ResourceAttributes.Public : ResourceAttributes.Private);
}
}
// 对非 DLL 的目标文件,必须要求有一个入口点
if (targetKind != PEFileKinds.Dll && !entryPointSet) {
sink.AddError("", string.Format("Need an entry point for target kind {0}", targetKind), String.Empty, CodeSpan.Empty, -1, Severity.Error);
}
// 最终产生输出的程序集
assemblyGen.Dump();
}
这段代码中,调用到了 PythonCompiler 类自身的私有方法 CompilePythonModule() 来完成编译模块的功能。下面我们来看一下这个方法在做什么:
private void CompilePythonModule(string fileName, PythonCompilerSink sink, bool createMain) {
// 设定当前要编译的源文件
assemblyGen.SetPythonSourceFile(fileName);
// 创建编译器环境对象
CompilerContext context = new CompilerContext(fileName, sink);
// 创建分析器
Parser p = Parser.FromFile(state, context);
// 调用分析器的分析方法,得到一个语句对象(语句应该是利用了组合模式的一个嵌套的概念,这个语句代表整个文件里的一个大语句)
Statement body = p.ParseFileInput();
if (sink.Errors > 0) return;
// 创建一个全局套件??有可能是指 globals() 这个字典对象。有待分析。。。
// 这里面的 Binder 是干什么的也有待研究。
GlobalSuite gs = Compiler.Ast.Binder.Bind(body, context);
string moduleName = GetModuleFromFilename(fileName);
// 这里看到了 TypeGen,该类代表一个类型产生器
// tg 指向了一个模块类型(IronPython 中,每一个模块产生为一个对应的类。)
TypeGen tg = OutputGenerator.GenerateModuleType(moduleName, assemblyGen);
// 编译模块的 __init__ 方法??(猜测)
CodeGen init = CompileModuleInit(context, gs, tg, moduleName);
// 如果需要创建 Main 方法则创建之
if (createMain) {
// 联想前面一个 CodeGen 的例子,观察调用语句可以想到,方法的产生器是
// CodeGen,而类型的产生器是 TypeGen
CodeGen main = OutputGenerator.GenerateModuleEntryPoint(tg, init, moduleName, referencedAssemblies);
// 这里注意到 CodeGen 代码产生器含有一个重要的属性就是代表这个方法
// 的反射信息的 MethodInfo, 可以通过这个来调用产生的方法。
assemblyGen.SetEntryPoint(main.MethodInfo, targetKind);
}
// 因为模块类不是普通的类,需要给他添加一个特殊的标签(Attribute)
assemblyGen.AddPythonModuleAttribute(tg, moduleName);
// 产生类型的动作完毕
tg.FinishType();
}
在上述两个方法中,我们看到,出现了几个重要的类,它们将是我们下面接着分析的重点线索:
Parser: 分析器
Statement: 语句
GlobalSuite: globals() ??
TypeGen: 类型产生器
CodeGen: 代码产生器(用于产生方法的代码)
在另一个私有方法 CompileModuleUnit 中,主要是进行了一些模块的导入工作,代码很容易懂,这里不详细分析。
现在回头看一下,在 IronPython/Hosting/PythonCompiler.cs 这个文件中,还剩下了两个类没有提到:
ResourceFile: 代表一个资源文件的相关属性。
PythonCompilerSink: 按照字面理解就是 PythonCompiler 的编译结果的接受池。至于它到底如何运作,留待后面再分析。
到这里为止,我们大致上看到了 IronPython 编译器的工作流程,从一系列源代码文件,资源文件,以及其他一些配置属性出发,经过 Parser, 各种 Generator 的运作,最终到达 AssemblyGenerator 的 Dump() 方法,输出编译结果程序集。
以上的代码分析难免有错误之处,尚有待继续挖掘和梳理。
出处:http://www.cnblogs.com/RChen/archive/2006/10/09/ipysrcstudy1.html
关注我】。(●'◡'●)
如果,您希望更容易地发现我的新博客,不妨点击一下绿色通道的【因为,我的写作热情也离不开您的肯定与支持,感谢您的阅读,我是【Jack_孟】!
本文来自博客园,作者:jack_Meng,转载请注明原文链接:https://www.cnblogs.com/mq0036/p/5014268.html
【免责声明】本文来自源于网络,如涉及版权或侵权问题,请及时联系我们,我们将第一时间删除或更改!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2014-12-02 C# 实现程序只启动一次(总结)
2014-12-02 C# 实现程序只启动一次(实现程序自重启)
2014-12-02 C# 实现程序只启动一次(多次运行激活第一个实例,使其获得焦点,并设置窗口在最前端显示)
2014-12-02 C#检测应用程序重复启动----函数检测(可以在多用户登录情况下检测)
2014-12-02 C#防止程序多次运行
2014-12-02 给System.Timer类的Elapsed事件加锁
2014-12-02 手缝针法大全--缝补衣服针法(图文讲解,转自汉服制作研习吧)