NAME
perlcompile - 关 于 Perl 编 译 器 和 翻 译 器 的 介 绍
DESCRIPTION 描 述
Perl 一 直 是 有 一 个 编 译 器 的 : 你 的 源 文 件 会 被 编 译 成 一 种 内 部 格 式 ( 一 种 语 法 分 析 树 ) , 并 且 在 运 行 前 还 会 被 优 化 。 从 5.005版 本 起 , Perl 在 发 行 时 就 带 有 一 个 模 块 可 以 检 查 优 化 过 的 语 法 分 析 树 ( 该 模 块 称 作 B模 块 ("B")) , 它 被 用 来 编 写 许 多 有 用 的 功 能 , 包 括 一 个 可 以 将 你 的 Perl转 成 C源 代 码 的 模 块 , 这 样 再 编 译 后 就 可 以 得 到 一 个 可 执 行 的 文 件 了 。
"B" 模 块 提 供 了 访 问 语 法 分 析 树 的 方 法 , 其 它 的 一 些 模 块 ( “后 端 ”) 则 对 这 个 树 进 行 操 作 。 一 些 把 它 ( 语 法 树 ) 以 字 节 码 的 形 式 输 出 , 还 有 以 C源 代 码 形 式 的 输 出 的 , 后 者 以 半 可 读 的 文 本 形 式 输 出 的 。 另 一 些 遍 历 整 棵 语 法 树 以 建 立 一 个 关 于 所 使 用 的 子 程 序 , 格 式 及 变 量 的 交 叉 引 用 表 。 还 有 另 外 一 些 检 查 你 的 代 码 , 看 看 有 没 有 模 棱 两 可 的 构 造 。 另 一 些 则 重 新 将 语 法 树 导 出 成 Perl代 码 , 可 以 起 代 码 美 化 或 是 消 除 混 乱 的 代 码 的 作 用 。 因 为 "B" 模 块 的 最 初 目 的 是 提 供 一 种 能 将 Perl程 序 转 为 对 应 C代 码 的 方 法 , 接 着 就 能 把 它 变 成 可 执 行 文 件 了 , 所 以 "B" 模 块 和 它 的 那 些 后 端 模 块 就 被 认 为 是 “编 译 器 ”了 , 即 使 它 们 实 际 上 没 有 做 任 何 编 译 方 面 的 事 。 这 个 编 译 器 的 各 个 部 分 精 确 的 说 应 该 是 个 “翻 译 器 ”, 或 者 一 个 “检 视 器 ”, 但 是 用 Perl的 人 们 想 要 一 个 “编 译 选 项 ”而 不 是 一 个 叫 做 “检 视 器 ”的 小 玩 艺 。 你 能 怎 么 办 呢 ? 这 篇 文 章 的 主 要 内 容 是 讲 Perl编 译 器 的 用 法 : 它 包 含 的 模 块 , 怎 样 使 用 那 些 最 重 要 的 后 端 模 块 , 它 们 有 什 么 问 题 , 如 何 让 它 们 工 作 。
Layout
布 局 编 译 器 的
后 端 放 在 "B::"
里 面 , 而 前 端
( 就 是 你 , 编
译 器 的 使 用 者
, 有 时 候 要 与
之 交 互 的 ) 是 O
模 块 。 一 些 后
端 ( 如
"B::C")) 提 供
了 一 些 程 序 (
如 perlcc) 来 隐 藏
模 块 的 复 杂 性
。 这 里 是 一 些
值 得 知 道 的 重
要 后 端 , 并 附
有 它 们 目 前 的
状 态 , 用 0到 10的
整 数 表 示 。 (
状 态 0表 示 目 前
该 部 分 功 能 只
是 有 一 个 框 架
, 还 没 有 实 现
; 状 态 10则 表 示
如 果 还 有 Bug的 话
, 我 们 会 感 到
很 奇 怪 的 ) :
B::Bytecode 将 语 法 树 存
成 机 器 相 关 的
格 式 , 可 供
BtyeLoader模 块 可 以 在
以 后 重 新 装 入
。 状 态 : 5( 一
些 部 分 可 以 工
作 , 一 些 不 可
以 , 还 有 一 些
还 没 有 测 试 )
B::C 创 建 C代 码 文 件
, 其 中 包 括 了
重 建 语 法 树 和
恢 复 解 释 器 的
代 码 。 状 态 :
6( 许 多 情 况 下
可 以 正 常 工 作
, 包 括 使 用 了
Tk的 程 序 ) 。
B::CC 按 照 语 法 树 中
运 行 期 代 码 的
路 径 创 建 C代 码
文 件 。 这 是 最
像 Perl - C 翻 译 器 的
一 个 , 但 是 它
生 成 的 代 码 几
乎 是 不 能 看 懂
的 , 因 为 它 把
语 法 树 翻 译 成
了 一 个 巨 大 的
switch结 构 来 操 作
Perl中 的 结 构 。 最
终 的 目 的 是 在
perl程 序 中 给 出 足
够 的 类 型 信 息
后 , 可 以 将 perl 数
据 结 构 的 操 作
转 换 为 c 级 别 的
数 据 结 构 , 对 int
和 float 的 操 作 。 状
态 : 5 (有 些 可 以
工 作 , 包 括 不
复 杂 的 Tk 示 例 ).
B::Lint 当 发 现 你 的
代 码 中 有 模 棱
两 可 的 构 造 时
会 发 出 警 告 。
状 态 : 6( 许 多
情 况 下 可 以 正
常 工 作 , 仅 仅
在 很 少 数 的 领
域 内 它 会 停 止
工 作 ) 。
B::Deparse 重 新 生 成 Perl代
码 , 试 着 把 代
码 用 一 致 的 格
式 写 出 来 。 状
态 : 8( 它 工 作
得 很 好 , 只 是
会 略 去 一 些 晦
涩 难 懂 的 部 分
) 。
B::Xref 生 成 关 于 申
明 和 关 于 变 量
以 及 子 程 序 的
使 用 情 况 的 报
告 。 状 态 : 8(
它 工 作 得 很 好
, 只 是 仍 有 一
点 延 迟 方 面 的
bugs) 。
Using The Back Ends 使 用 后 端
接 下 来 的 部 分 介 绍 怎 样 使 用 各 种 各 样 的 编 译 器 后 端 。 介 绍 的 顺 序 按 照 后 端 的 成 熟 程 度 排 列 , 所 以 最 为 稳 定 的 , 经 过 了 验 证 的 后 端 会 最 先 介 绍 , 还 在 试 验 中 和 没 有 完 成 的 后 端 就 放 到 后 面 描 述 了 。
O模 块 默 认 让 -c 开 关 有 效 , 这 防 止 Perl在 编 译 完 代 码 后 运 行 程 序 。 这 也 是 为 什 么 所 有 的 后 端 在 产 生 任 何 输 出 前 都 会 打 印 一 句 :
myperlprogram syntax OK
The Cross Referencing Back End 交 叉 引 用 后 端 交 叉 引 用 后 端 ( B::Xref) 生 成 一 个 关 于 你 的 程 序 的 报 表 , 把 各 个 申 明 以 及 子 程 序 , 变 量 ( 包 括 格 式 ) 的 使 用 情 况 存 入 文 件 中 去 。 举 例 来 说 , 这 有 一 段 摘 自 对 pod2man程 序 分 析 后 生 成 的 报 表 ( 该 程 序 是 Perl自 带 的 一 个 例 程 ) :
Subroutine clear_noremap Package (lexical) $ready_to_print i1069, 1079 Package main $& 1086 $. 1086 $0 1086 $1 1087 $2 1085, 1085 $3 1085, 1085 $ARGV 1086 %HTML_Escapes 1085, 1085这 里 展 示 了
"clear_noremap" 子 程 序 中 变 量 的 使 用 情 况 。 就 像 变 量 $ready_to_print 是 my() (词 法 ) 的 一 个 变 量 , 在 第 1069行 被 引 入 ( 原 文 用 的 词 是 introduced, 也 就 是 在 my() 中 第 一 次 被 定 义 的 意 思 ), 然 后 在 第 1079行 该 变 量 被 使 用 了 。 从 主 包 ( main package) 中 来 的 变 量 $& 又 在 第 1086行 被 使 用 , 等 等 。 行 号 前 面 可 能 会 有 一 个 字 母 作 为 前 缀 , 它 们 的 意 思 是 :
i 变 量 首 次 被 引 入 |
(在 my()中 申 明 ) 。 |
||
& 子 程 序 或 者 方 法 的 |
引 用 。
s 定 义 的 子 程 序 。 | |
r 定 义 的 格 式 。 交 叉 引 用 中 最 为 有 用 的 选 项 就 是 把 报 表 存 入 不 同 的 文 件 , 例 如 要 把 关 于 |
myperlprogram 的 报 表 存 入 文 件 report 中 :
$ perl -MO=Xref,-oreport myperlprogram
The Decompiling Back End 反 编 译 后 端 反 编 译 后 端 将 把 你 的 Perl语 法 树 重 新 变 成 源 代 码 。 生 成 的 源 代 码 会 按 照 某 种 格 式 组 织 , 所 以 这 个 后 端 可 以 用 来 消 除 代 码 中 的 混 乱 部 分 。 此 后 端 的 基 本 使 用 方 法 如 下 :
$ perl -MO=Deparse myperlprogram 你 也 许 马 上 会 发 现 Perl并 不 知 道 如 何 给 你 的 代 码 分 段 。 你 要 自 己 手 动 添 入 新 行 来 把 这 大 断 的 代 码 分 开 。 然 而 现 在 , 让 我 们 看 看 代 码 只 有 一 行 时 情 况 怎 样 , 这 个 后 端 会 做 些 什 么 :
$ perl -MO=Deparse -e ’$op=shift⎪⎪die "usage: $0 code [...]";chomp(@ARGV=<>)unless@ARGV; for(@ARGV){$was=$_;eval$op; die$@ if$@; rename$was,$_ unless$was eq $_}’ -e syntax OK $op = shift @ARGV ⎪⎪ die("usage: $0 code [...]"); chomp(@ARGV = <ARGV>) unless @ARGV; foreach $_ (@ARGV) { $was = $_; eval $op; die $@ if $@; rename $was, $_ unless $was eq $_;}这 个 后 端 也 有 几 条 选 项 控 制 生 成 的 代 码 , 举 例 说 , 你 可 以 把 缩 进 的 尺 寸 设 在
4( 最 大 ) 到 2之 间 :
$ perl -MO=Deparse,-si2 myperlprogram
-p 开 关 控 制 在 常 常 可 以 不 加 圆 括 号 的 地 方 加 上 它 们 :
$ perl -MO=Deparse -e ’print "Hello, world\n"’ -e syntax OK print "Hello, world\n"; $ perl -MO=Deparse,-p -e ’print "Hello, world\n"’ -e syntax OK print("Hello, world\n");要 知 道 更 多 , 请 参 考
B::Deparse
Lint 后 端
lint 后 端 ( B::Lint) 检 察 程 序 中 不 好 的 程 序 风 格 。 一 个 程 序 认 为 的 不 好 风 格 可 能 对 另 外 一 个 程 序 员 来 说 是 用 起 来 很 有 效 的 工 具 , 所 以 有 选 项 让 你 设 定 哪 些 东 东 将 会 受 到 检 查 。 要 运 行 一 个 风 格 检 查 器 检 察 你 的 代 码 :
$ perl -MO=Lint myperlprogram 要 取 消 对 上 下 文 和 没 有 定 义 的 子 程 序 的 检 查 :
$ perl -MO=Lint,-context,-undefined-subs myperlprogram 要 知 道 更 多 的 选 项 信 息 , 请 看 B::Lint
The Simple C Back End 简 化 的 C后 端 这 个 模 块 用 来 把 你 的 Perl程 序 的 内 部 编 译 状 态 存 储 到 一 个 C代 码 文 件 中 去 , 而 生 成 的 C代 码 就 可 以 被 特 定 平 台 上 的 C编 译 器 转 换 成 一 个 可 执 行 文 件 了 。 最 后 的 程 序 还 会 和 Perl解 释 器 的 库 文 件 静 态 链 接 起 来 , 所 以 它 不 会 节 省 你 的 磁 盘 空 间 ( 除 非 你 的 Perl是 用 共 享 的 库 文 件 创 建 的 ) 或 是 程 序 大 小 , 然 而 , 另 一 方 面 , 程 序 启 动 起 来 会 快 一 些 。
"perlcc" 工 具 缺 省 是 生 成 以 下 的 可 执 行 文 件 。
perlcc myperlprogram.pl
The Bytecode Back End 字 节 码 后 端 这 个 模 块 只 有 在 你 能 够 找 到 一 种 方 法 来 装 入 并 运 行 它 生 成 的 字 节 码 时 才 会 显 得 有 用 。 ByteLoader模 块 提 供 了 这 项 功 能 。 要 把 Perl转 换 成 可 执 行 的 字 节 码 , 你 可 以 使 用 "perlcc" 的 "-B" 开 关 :
perlcc -B myperlprogram.pl 字 节 码 是 和 机 器 类 型 无 关 的 , 所 以 一 旦 你 编 译 了 一 个 模 块 或 是 程 序 , 它 就 可 以 像 Perl源 代 码 一 样 具 有 可 移 植 性 。 ( 假 设 那 个 模 块 或 者 程 序 的 使 用 者 有 一 个 足 够 新 的 Perl解 释 器 来 对 字 节 码 进 行 解 码 ) 有 一 些 选 项 用 来 控 制 要 生 成 的 字 节 码 的 性 质 和 关 于 优 化 方 面 的 参 数 , 要 知 道 这 些 选 项 的 详 细 情 况 , 请 参 考 B::Bytecode
The Optimized C Back End 优 化 的 C后 端 优 化 的 C后 端 按 照 语 法 树 中 运 行 期 代 码 的 路 径 将 你 的 Perl程 序 转 换 成 等 效 的 (但 是 被 优 化 了 的 )C代 码 文 件 。 这 个 C程 序 会 直 接 对 Perl的 数 据 结 构 进 行 操 作 , 而 且 也 会 链 接 Perl的 解 释 器 的 库 文 件 , 以 支 持 eval(), "s///e", "require" 等 等 。
"perlcc" 工 具 使 用 -O 开 关 生 成 这 种 可 执 行 文 件 。 要 编 译 一 个 Perl程 序 ( 以 ".pl" 或 者 ".p" 结 尾 ) :
perlcc -O myperlprogram.pl 从 Perl模 块 创 建 一 个 共 享 库 文 件 ( 以 ".pm" 结 尾 ) :
perlcc -O Myperlmodule.pm 知 道 更 多 , 请 参 考 perlcc 和 B::CC.
Module List for the Compiler Suite 编 译 套 件 的 模 块 列 表
B 这 个 模 块 是 一 个 自 省 的 ( |
introspective, 用 Java的 术 语 说 就 是 |
“reflective”) 模 块 , 允 许 Perl程 序 审 视 自 己 的 内 部 。 后 端 模 块 都 是 通 过 这 个 模 块 来 访 问 语 法 分 析 树 的 。 而 你 , 后 端 模 块 的 用 户 , 就 不 用 和 B模 块 打 交 道 了 。
O 这 个 模 块 是 编 译 器 的 那 些 后 端 的 前 端 , 一 般 像 这 样 进 行 调 用 : |
$ perl -MO=Deparse myperlprogram 这 与 在 这 个 Perl程 序 中 使 用 "use O ’Deparse’" 相 同 。
B::Asmdata 这 个
模 块 被 B::Assembler 模 块
使 用 , 而 B::Assembler 又
接 着 被 B::Bytecode 模 块
使 用 , B::Bytecode中 有
一 个 字 节 码 形
式 存 放 的 语 法
分 析 树 以 便 以
后 装 入 。 B::Asmdata自
己 并 不 算 是 一
个 后 端 , 也 许
说 它 是 后 端 的
一 个 组 件 比 较
好 。
B::Assembler 这 个 模 块 可
以 将 语 法 树 转
为 适 合 存 储 和
恢 复 的 数 据 形
式 。 它 本 身 不
是 一 个 后 端 ,
但 是 算 是 某 个
后 端 的 一 个 组
件 。 assemble 程 序 用
它 来 生 成 字 节
码 。
B::Bblock 这 个 模 块 被 B::CC
后 端 使 用 。 它
被 用 来 运 行 “基
本 块 ”。 一 个 基
本 块 就 是 一 段
从 头 到 尾 的 操
作 , 中 间 是 不
可 能 停 下 来 或
出 现 分 支 的 。
B::Bytecode 这 个 模 块 可
以 由 程 序 的 语
法 树 生 成 字 节
码 。 生 成 的 字
节 码 会 被 写 入
到 文 件 中 , 以
后 还 能 被 重 新
恢 复 成 语 法 树
。 总 的 目 标 就
是 为 了 只 进 行
一 次 费 时 的 程
序 编 译 工 作 ,
然 后 把 解 释 器
的 状 态 存 入 文
件 中 , 运 行 程
序 时 再 把 状 态
从 文 件 中 恢 复
。 具 体 的 用 法
请 参 考 "The Bytecode Back End"
。
B::C 这 个 模 块 按 照
语 法 树 和 其 他
一 些 解 释 器 的
内 部 数 据 结 构
生 成 C代 码 。 然
后 你 再 编 译 生
成 的 C代 码 , 就
可 以 得 到 一 个
可 执 行 文 件 了
。 运 行 时 这 个
可 执 行 文 件 会
恢 复 解 释 器 和
内 部 的 数 据 结
构 来 转 动 程 序
。 要 知 道 细 节
请 参 考 "The Simple C Back
End"。
B::CC 这 个 模 块 按 照
你 程 序 中 的 操
作 生 成 C代 码 。
不 像 B::C 模 块 只 是
把 解 释 和 它 的
状 态 存 入 C程 序
中 , B::CC 模 块 生 成
的 是 不 包 含 解
释 器 的 C 程 序 ,
所 以 用 B::CC 翻 译 的
C 程 序 运 行 速 度
比 一 般 的 解 释
执 行 的 程 序 速
度 要 快 , 具 体
用 法 请 参 考 "The
Optimized C Back End" 。
B::Concise 这 个 模 块 输
出 一 个 简 洁 的
(但 是 完 整 的 ) Perl
分 析 树 。 它 的
输 出 比 B::Terse 或 者
B::Debug 的 结 果 更 容
易 定 制 (并 且 也
可 以 模 仿 它 们
)。 这 个 模 块 对
书 写 自 己 的 后
端 , 或 者 学 习 Perl
实 现 的 人 有 用
。 它 对 一 般 的
程 序 员 没 有 用
处 。
B::Debug 这 个 模 块 把
Perl语 法 分 析 树 非
常 详 细 地 输 出
到 标 准 输 出 上
去 。 这 对 正 在
编 写 自 己 的 后
端 程 序 , 或 正
在 深 入 Perl内 部 机
制 的 人 们 来 说
是 非 常 有 用 的
。 对 普 通 程 序
员 来 说 则 没 什
么 用 。
B::Deparse 这 个 模 块 将
编 译 了 的 语 法
树 反 向 分 析 得
出 Perl源 代 码 , 这
在 调 试 或 是 反
编 译 他 人 代 码
的 时 候 会 是 非
常 有 用 的 。 另
外 让 它 为 你 自
己 的 代 码 做 一
些 美 化 工 作 也
是 可 以 的 。 要
知 道 细 节 请 参
考 "The Decompiling Back End"。
B::Disassembler 这 个 模 块 把
字 节 码 恢 复 成
语 法 树 , 它 本
身 不 是 一 个 后
端 , 而 是 某 个
后 端 的 一 个 组
件 。 它 会 被 和
字 节 码 在 一 起
的 disassemble 程 序 使 用
。
B::Lint 这 个 模 块 审
视 你 的 代 码 编
译 后 的 格 式 ,
并 且 找 到 那 些
容 易 让 人 皱 眉
, 却 又 不 至 于
引 起 警 告 的 地
方 。 举 例 来 说
, 使 用 一 个 标
量 内 容 ( scalar context)
的 数 组 , 而 不
显 式 地 申 明 成
"scalar(@array)" 。 这
种 情 况 是 会 被 Lint
标 示 出 来 的 。
要 知 道 细 节 请
参 考 "The Lint Back End"。
B::Showlex 这 个 模 块 打
印 出 my() 中 的 变
量 在 函 数 或 是
文 件 中 的 使 用
情 况 , 以 得 到
一 份 关 于 my() 中
的 变 量 在 定 义
于 文 件 myperlprogram 中 的
子 程 序 mysub() 中 的
使 用 情 况 的 列
表 :
$ perl -MO=Showlex,mysub myperlprogram 要 得 到 一 份 关 于 my() 中 的 变 量 在 文 件 myperlprogram中 的 使 用 情 况 的 列 表 :
$ perl -MO=Showlex myperlprogram
[ BROKEN ]
B::Stackobj 这 个
模 块 被 B::CC 模 块 调
用 。 它 本 身 不
是 后 端 , 但 是
是 某 个 后 端 的
一 个 组 件 。
B::Stash 这 个 模 块 被 perlcc
程 序 调 用 , 而
perlcc可 以 把 一 个 模
块 编 译 成 可 执
行 文 件 。 B::Stash 把
程 序 使 用 的 符
号 表 打 印 出 来
, 并 被 用 来 阻
止 B::CC 为 B::* 或 是 O 模
块 生 成 C 代 码 。
它 本 身 不 是 后
端 , 但 是 是 某
个 后 端 的 一 个
组 件 。
B::Terse 这 个 模 块 用
来 打 印 语 法 树
的 内 容 , 但 是
信 息 不 会 有
B::Debug打 印 的 那 么
多 。 对 比 来 说
, "print "Hello,
world."" 会 让 B::Debug 产
生 96行 输 出 , 但
是 B::Terse只 会 有 6行
。 这 个 模 块 对
正 在 编 写 自 己
的 后 端 程 序 ,
或 正 在 深 入 Perl内
部 机 制 的 人 们
来 说 是 非 常 有
用 的 。 对 普 通
程 序 员 来 说 则
没 什 么 用 。
B::Xref 这 个 模 块 打
印 一 个 报 表 列
出 在 程 序 中 哪
里 定 义 和 使 用
了 哪 些 变 量 ,
子 程 序 或 格 式
, 报 表 还 会 列
出 程 序 装 入 的
模 块 。 要 知 道
详 细 的 使 用 方
法 , 请 参 考 "The Cross
Referencing Back End" 。
KNOWN PROBLEMS 已 知 的 问 题
简 单 C 后 端 目 前 只 保 存 以 字 符 和 数 字 命 名 的 类 型 说 明 优 化 的 C 后 端 会 为 一 些 不 该 为 之 输 出 的 模 块 ( 比 如 说 DirHandle) 输 出 代 码 。 而 且 它 不 太 可 能 正 确 地 处 理 正 在 执 行 的 子 程 序 外 部 的 goto语 句 ( goto &sub is OK) 。 目 前 "goto LABEL" 语 句 在 这 个 后 端 中 完 全 不 会 工 作 。 他 还 会 生 成 让 C 编 译 器 头 痛 无 比 的 巨 大 的 初 始 化 函 数 。 如 果 把 这 个 初 始 化 函 数 分 割 开 是 能 得 到 比 目 前 更 好 的 效 果 的 。 另 外 的 问 题 包 括 : 处 理 无 符 号 的 数 学 问 题 时 不 能 正 确 工 作 ; 一 些 操 作 码 如 果 按 照 默 认 的 操 作 码 机 制 处 理 也 会 有 非 正 常 的 结 果 。
BEGIN{} 块 会 在 编 译 你 的 代 码 的 时 候 被 执 行 。 所 有 的 在 BEGIN{} 中 初 始 化 的 外 部 状 态 , 如 打 开 的 文 件 , 初 始 的 数 据 库 连 结 等 等 , 会 有 不 正 确 的 表 现 。 为 了 解 决 这 个 问 题 , Perl中 又 提 供 了 一 个 INIT{} 块 来 对 应 程 序 编 译 之 后 , 正 式 运 行 之 前 要 执 行 的 那 段 代 码 。 执 行 的 顺 序 是 : BEGIN{}, (后 端 编 译 程 序 可 能 这 时 会 保 存 状 态 ), INIT{}, 程 序 运 行 , END{}。
AUTHOR 作 者
这 篇 文 章 最 初 是 由 Nathan Torkington 编 写 , 现 在 由 邮 件 列 表 (perl5-porters [AT] perl.org.)维 护
译 者
郭 锐 (sunny65535) <sunny65535 [AT] 263.net> 跋 本 页 面 中 文 版 由 中 文 man 手 册 页 计 划 提 供 。 中 文 man 手 册 页 计 划 : https://github.com/man-pages-zh/manpages-zh