1.理解前端编译的规格

在正式阅读OpenJDK前端编译器源码之前,建议先阅读笔者的这几篇文章,可以对JVM的前端编译的规格有一定了解:

【编译引擎】 1 学习阅读Class文件结构的意义

【编译引擎】-2-学习阅读Class文件结构(16进制版)-上

【编译引擎】-2-学习阅读Class文件结构(16进制版)-中

【编译引擎】-2-学习阅读Class文件结构(16进制版)-下

【编译引擎】-3-学习阅读Class文件结构(javap版)

【编译引擎】-4-学习阅读Class文件指令-概览

当我们对前端编译器的规格、术语、概念有了一定了解后,就可以阅读如下几个基础模块的代码,建立阅读前端编译器核心代码的基础。

2.前端编译器基础模块1-javac的入口类

这部分代码可以参考这篇文章中的"3.javac的入口解读"章节,此处不赘述

【宏观】如何更加深入理解Java虚拟机

3.前端编译器基础模块2-参数管理

javac主要通过如下三个类实现了参数管理:

com/sun/tools/javac/main/Option.java

com/sun/tools/javac/main/OptionHelper.java

com/sun/tools/javac/util/Options.java

3.1.com/sun/tools/javac/main/Option.java

Option是一个枚举类,是参数管理的核心类

从关键属性看

Option的关键属性有:textkindgroupargsNameKeydescrKeychoiceKindchoices

具体见如下代码标注:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public enum Option {
    ……
        
    /**
     * HCZ:
     * 命令配置的text
     */
    public final String text;

    /**
     * HCZ:
     * javac的命令行参数配置分类,可选值:标准的、-X这种扩展的、javac的隐藏属性
     */
    final OptionKind kind;

    /**
     * HCZ:
     * javac的命令行参数配置分组,可选值:Basic、javac文件系统专有、信息类参数、操作类参数
     */
    final OptionGroup group;

    /** Documentation key for arguments.
     * HCZ:
     * 命令配置的argsNameKey
     */
    final String argsNameKey;

    /** Documentation key for description.
     * HCZ:
     * 命令配置的descrKey
     */
    final String descrKey;

    /** Suffix option (-foo=bar or -foo:bar)
     * HCZ:
     * 命令配置是否具有后缀
     */
    final boolean hasSuffix;

    /** The kind of choices for this option, if any.
     * HCZ:
     * javac的命令行参数配置的选择类型,可选值:其中任意1个、N个
     */
    final ChoiceKind choiceKind;

    /** The choices for this option, if any, and whether or not the choices
     * are hidden
     * HCZ:
     * 记录任1orN个选项的具体选项
     */
    final Map<String,Boolean> choices;
 	……   
}

从枚举值看

javac支持的命令行参数就是与Option类的枚举值对应的。笔者将javac官方文档与Option类的枚举值进行了对应:

javac官方文档:https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javac.html

javac文档中的Standard Options、Cross-Compilation Options、Compact Profile Option

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
Standard Options
	-Akey[=value]
        A("-A", "opt.arg.key.equals.value", "opt.A", STANDARD, BASIC, true) {
    -cp path or -classpath path
        CLASSPATH("-classpath", "opt.arg.path", "opt.classpath", STANDARD, FILEMANAGER),
        CP("-cp", "opt.arg.path", "opt.classpath", STANDARD, FILEMANAGER) {
    -Djava.ext.dirs=directories
        DJAVA_EXT_DIRS("-Djava.ext.dirs=", "opt.arg.dirs", "opt.extdirs", EXTENDED, FILEMANAGER) {
    -Djava.endorsed.dirs=directories
        DJAVA_ENDORSED_DIRS("-Djava.endorsed.dirs=", "opt.arg.dirs", "opt.endorseddirs", EXTENDED, FILEMANAGER) {
    -d directory
        D("-d", "opt.arg.directory", "opt.d", STANDARD, FILEMANAGER),
    -deprecation
        DEPRECATION("-deprecation", "opt.deprecation", STANDARD, BASIC) {
    -encoding encoding
        ENCODING("-encoding", "opt.arg.encoding", "opt.encoding", STANDARD, FILEMANAGER) {
    -endorseddirs directories
        ENDORSEDDIRS("-endorseddirs", "opt.arg.dirs", "opt.endorseddirs", STANDARD, FILEMANAGER),
    -extdirs directories
        EXTDIRS("-extdirs", "opt.arg.dirs", "opt.extdirs", STANDARD, FILEMANAGER),
    -g
        G("-g", "opt.g", STANDARD, BASIC),		
    -g:none
        G_NONE("-g:none", "opt.g.none", STANDARD, BASIC) {
    -g:[keyword list]
        G_CUSTOM("-g:",  "opt.g.lines.vars.source", STANDARD, BASIC, ANYOF, "lines", "vars", "source"),
    -help
        H("-h", "opt.arg.directory", "opt.headerDest", STANDARD, FILEMANAGER),
        HELP("-help", "opt.help", STANDARD, INFO) {
    -implicit:[class, none]
        IMPLICIT("-implicit:", "opt.implicit", STANDARD, BASIC, ONEOF, "none", "class"),
    -Joption
        J("-J", "opt.arg.flag", "opt.J", STANDARD, INFO, true) {
    -nowarn
        NOWARN("-nowarn", "opt.nowarn", STANDARD, BASIC) {
    -parameters
        PARAMETERS("-parameters","opt.parameters", STANDARD, BASIC),
    -proc: [none, only]
        PROC("-proc:", "opt.proc.none.only", STANDARD, BASIC,  ONEOF, "none", "only"),
    -processor class1 [,class2,class3...]
        PROCESSOR("-processor", "opt.arg.class.list", "opt.processor", STANDARD, BASIC),
    -processorpath path
        PROCESSORPATH("-processorpath", "opt.arg.path", "opt.processorpath", STANDARD, FILEMANAGER),
    -s dir
        S("-s", "opt.arg.directory", "opt.sourceDest", STANDARD, FILEMANAGER),
    -source release
        SOURCE("-source", "opt.arg.release", "opt.source", STANDARD, BASIC) {
    -sourcepath sourcepath
        SOURCEPATH("-sourcepath", "opt.arg.path", "opt.sourcepath", STANDARD, FILEMANAGER),
    -verbose
        VERBOSE("-verbose", "opt.verbose", STANDARD, BASIC),
    -version
        VERSION("-version", "opt.version", STANDARD, INFO) {
    -werror
        WERROR("-Werror", "opt.Werror", STANDARD, BASIC),
    -X
        X("-X", "opt.X", STANDARD, INFO) {

Cross-Compilation Options
    -target version
        TARGET("-target", "opt.arg.release", "opt.target", STANDARD, BASIC) {
    -bootclasspath bootclasspath
        BOOTCLASSPATH("-bootclasspath", "opt.arg.path", "opt.bootclasspath", STANDARD, FILEMANAGER) {

Compact Profile Option
    -profile
        PROFILE("-profile", "opt.arg.profile", "opt.profile", STANDARD, BASIC) {

javac官方文档的Nonstandard Options

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Nonstandard Options
    -Xbootclasspath/p:path
        XBOOTCLASSPATH_PREPEND("-Xbootclasspath/p:", "opt.arg.path", "opt.Xbootclasspath.p", EXTENDED, FILEMANAGER),
    -Xbootclasspath/a:path
        XBOOTCLASSPATH_APPEND("-Xbootclasspath/a:", "opt.arg.path", "opt.Xbootclasspath.a", EXTENDED, FILEMANAGER),
    -Xbootclasspath/:path
        XBOOTCLASSPATH("-Xbootclasspath:", "opt.arg.path", "opt.bootclasspath", EXTENDED, FILEMANAGER) {	
    -Xdoclint:[-]group [/access]
    -Xdoclint:all/protected
    -Xdoclint:all,-html/package
    -Xdoclint:none
    -Xdoclint:all[/access]
        XDOCLINT("-Xdoclint", "opt.Xdoclint", EXTENDED, BASIC),
        XDOCLINT_CUSTOM("-Xdoclint:", "opt.Xdoclint.subopts", "opt.Xdoclint.custom", EXTENDED, BASIC) {
    -Xlint
        XLINT("-Xlint", "opt.Xlint", EXTENDED, BASIC),
    -Xlint:all
    -Xlint:none
    -Xlint:name
    -Xlint:-name
        XLINT_CUSTOM("-Xlint:", "opt.Xlint.suboptlist", EXTENDED,   BASIC, ANYOF, getXLintChoices()),
    -Xmaxerrs number
        XMAXERRS("-Xmaxerrs", "opt.arg.number", "opt.maxerrs", EXTENDED, BASIC),
    -Xmaxwarns number
        XMAXWARNS("-Xmaxwarns", "opt.arg.number", "opt.maxwarns", EXTENDED, BASIC),
    -Xstdout filename
        XSTDOUT("-Xstdout", "opt.arg.file", "opt.Xstdout", EXTENDED, INFO) {
    -Xprefer:[newer,source]
        XPREFER("-Xprefer:", "opt.prefer", EXTENDED, BASIC, ONEOF, "source", "newer"),
    -Xpkginfo:[always,legacy,nonempty]
        XPKGINFO("-Xpkginfo:", "opt.pkginfo", EXTENDED, BASIC, ONEOF, "always", "legacy", "nonempty"),
    -Xprint
        XPRINT("-Xprint", "opt.print", EXTENDED, BASIC),
    -XprintProcessorInfo
        XPRINTPROCESSORINFO("-XprintProcessorInfo", "opt.printProcessorInfo", EXTENDED, BASIC),
    -XprintRounds
        XPRINTROUNDS("-XprintRounds", "opt.printRounds", EXTENDED, BASIC),

javac官方文档的Command-Line Argument Files

1
2
Command-Line Argument Files
	AT("@", "opt.arg.file", "opt.AT", STANDARD, INFO, true) {

javac官方文档上没有对应的枚举值的隐藏参数

除了前两个被定义为EXTEND类型的参数,后面的都是被定为为HIDDEN类型的参数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
PLUGIN("-Xplugin:", "opt.arg.plugin", "opt.plugin", EXTENDED, BASIC) {
XDIAGS("-Xdiags:", "opt.diags", EXTENDED, BASIC, ONEOF, "compact", "verbose"),
    
FULLVERSION("-fullversion", null, HIDDEN, INFO) {
DIAGS("-XDdiags=", null, HIDDEN, INFO) {
MOREINFO("-moreinfo", null, HIDDEN, BASIC) {
PROMPT("-prompt", null, HIDDEN, BASIC),
DOE("-doe", null, HIDDEN, BASIC),
PRINTSOURCE("-printsource", null, HIDDEN, BASIC),
WARNUNCHECKED("-warnunchecked", null, HIDDEN, BASIC) {
O("-O", null, HIDDEN, BASIC),
XJCOV("-Xjcov", null, HIDDEN, BASIC),
XD("-XD", null, HIDDEN, BASIC) {
SOURCEFILE("sourcefile", null, HIDDEN, INFO) {

image-20211013173627325

结论

从上述源码的分析,我们可以得到以下结论:

javac的命令行参数被分为了STANDARD、EXTENDED、HIDDEN。其中,

STANDARD参数会写进javac官方文档的Standard Options、Cross-Compilation Options、Compact Profile Option章节;

EXTENDED会写进javac官方文档Nonstandard Options章节;

延伸

其它JDK自带的命令行工具,都有类似的Option模块处理命令行参数(如下java.c源码中)。

源码中有如此机制,我们可以得到一下命令行工具学习的新思路:

我们可以在学习JDK命令行工具的时候,不仅局限于对应的官方文档,还可以通过Options类的枚举值探索一些隐藏的命令行参数。

image-20211014154700768

从构造函数看

如下N个构造函数,支撑了各个枚举值的定义。

image-20211014155636948

从方法看

Option的方法如下:

image-20211014155805308

关键方法是process方法,它通过将待处理的Option对象加入到OptionHelper中,支撑了2.1章节涉及的Main#processArgs方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* HCZ:
* 将待处理的option加入到OptionHelper对象
*/
public boolean process(OptionHelper helper, String option, String arg) {
    if (choices != null) {
        if (choiceKind == ChoiceKind.ONEOF) {
            // some clients like to see just one of option+choice set
            for (String s: choices.keySet())
                helper.remove(option + s);
            String opt = option + arg;
            helper.put(opt, opt);
            // some clients like to see option (without trailing ":")
            // set to arg
            String nm = option.substring(0, option.length() - 1);
            helper.put(nm, arg);
        } else {
            // set option+word for each word in arg
            for (String a: arg.split(",+")) {
                String opt = option + a;
                helper.put(opt, opt);
            }
        }
    }
    helper.put(option, arg);
    return false;
}

从周边关系看

在Main#optionHelper中,实现了OptionHelper的匿名类 在Main#options中,维护了Options对象

image-20211014160156265

3.2.OptionHelper和Options

com/sun/tools/javac/util/Options.java和com/sun/tools/javac/main/OptionHelper.java两个类都是Option类的辅助类,

主要是一些集合的CRUD操作,读者有兴趣可以自行阅读。

4.总结

  • 本文回顾了JVM前端编译器的规格。
  • 本文分析了前端编译器的基础模块—javac的入口模块
  • 本文分析了前端编译器的基础模块——参数配置模块