Manpages

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_printmy() (词 法 ) 的 一 个 变 量 , 在 第 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>