javac编译流程(1)准备阶段

序言

因为本系列属于我自己独立书写,没有任何成系统的参考资料和教程,基本上可以说是生啃源码,肯定存在不足和纰漏,请多多谅解,本系列基于jdk9,如果和读者手上的版本不同,可以互相参考,原理是相通的,主要的说明我会放到代码的注释中,便于理解

首先,第一步,进入main方法中

import com.sun.tools.javac.Main;
public class Test{
    public static void main(String[] args) throws Exception {
        Main m=new Main();
        m.main(args);
    }
}

点开main方法,各种包装过程我就不赘述了,直接进入准备对应的代码

public static int compile(String[] args) {
        com.sun.tools.javac.main.Main compiler =
            new com.sun.tools.javac.main.Main("javac");
            //设置为Javac指令
        return compiler.compile(args).exitCode;
        //返回值为最终结束状态码
    }
public Result compile(String[] args) {
        Context context = new Context();
        //这是一个容器,用来存储运行过程中需要的各种参数和对象
        //其实就是包装过的HashMap
        JavacFileManager.preRegister(context); 
        //对context容器进行第一次设置
        Result result = compile(args, context);
        //主要方法
        if (fileManager instanceof JavacFileManager) {
            try {
                // A fresh context was created above, so jfm must be a JavacFileManager
                ((JavacFileManager)fileManager).close();
            } catch (IOException ex) {
                bugMessage(ex);
            }
        }
        return result;
    }

进入JavacFileManager.preRegister(context);

public static void preRegister(Context context) {
		//创建一个JavacFileManager对象放入context容器
		//并通过构造方法继续对context容器进行设置
        context.put(JavaFileManager.class,
                (Factory<JavaFileManager>)c -> new JavacFileManager(c, true, null));
                //Factory<JavaFileManager>用于延迟加载
    }

进入JavacFileManager构造方法

public JavacFileManager(Context context, boolean register, Charset charset) {
        super(charset);
        if (register)
        //将context中对应的元素进行覆盖,消除前面延迟加载的影响
            context.put(JavaFileManager.class, this);
        //对本类进行设置,这部分内容我留到后面再提,因为如果输入为空
        //JavacFileManager类根本不会被赋值,也根本不会调用这个方法
        //其次,我们需要先讲解后面的代码,才能讲清楚这一段
        setContext(context);
    }

回到上文,进入compile(args, context);

public Result compile(String[] args) {
        Context context = new Context();
        JavacFileManager.preRegister(context); // can't create it until Log has been set up
        //进入这里
        Result result = compile(args, context);
        if (fileManager instanceof JavacFileManager) {
            try {
                // A fresh context was created above, so jfm must be a JavacFileManager
                ((JavacFileManager)fileManager).close();
            } catch (IOException ex) {
                bugMessage(ex);
            }
        }
        return result;
    }

开始准备阶段

public Result compile(String[] argv, Context context) {
		//如果我们设置了初始输出流
        if (stdOut != null) {
            context.put(Log.outKey, stdOut);
        }
		//如果我们设置了异常输出流
        if (stdErr != null) {
            context.put(Log.errKey, stdErr);
        }
		//获取日志类,并做进一步的设置
        log = Log.instance(context);

进入Log.instance(context)

public static Log instance(Context context) {
        Log instance = context.get(logKey);
        //如果我们已经设置了日志类
        if (instance == null)
        	//在构造方法中对context进一步设置
            instance = new Log(context);
        return instance;
    }
protected Log(Context context) {
		//initWriters(context)是对日志类进一步设置的方法
        this(context, initWriters(context));
    }
private static Map<WriterKind, PrintWriter> initWriters(Context context) {
        PrintWriter out = context.get(outKey);
        PrintWriter err = context.get(errKey);
        //若为设置默认输出流和错误输出流,设为系统默认流
        if (out == null && err == null) {
            out = new PrintWriter(System.out, true);
            err = new PrintWriter(System.err, true);
            return initWriters(out, err);
        //进行健壮性判断
        } else if (out == null || err == null) {
            PrintWriter pw = (out != null) ? out : err;
            return initWriters(pw, pw);
        } else {
            return initWriters(out, err);
        }
    }
private static Map<WriterKind, PrintWriter> initWriters(PrintWriter out, PrintWriter err) {
		//主要是设置日志类内部集合中常量和流的对应关系,没什么好说的,
        Map<WriterKind, PrintWriter> writers = new EnumMap<>(WriterKind.class);
        writers.put(WriterKind.ERROR, err);
        writers.put(WriterKind.WARNING, err);
        writers.put(WriterKind.NOTICE, err);

        writers.put(WriterKind.STDOUT, out);
        writers.put(WriterKind.STDERR, err);

        return writers;
    }

回到this方法,其实没什么好讲的,无非是尝试到context取值填充到日志类中,后面遇到了我再详细讲解对应的值的含义

private Log(Context context, Map<WriterKind, PrintWriter> writers) {
        super(JCDiagnostic.Factory.instance(context));
        //将本类(日志类)放入context中
        context.put(logKey, this);
        //将前文设置的常量和流对应集合放入本类成员变量中
        this.writers = writers;

        @SuppressWarnings("unchecked") // FIXME
        DiagnosticListener<? super JavaFileObject> dl =
            context.get(DiagnosticListener.class);
        this.diagListener = dl;

        diagnosticHandler = new DefaultDiagnosticHandler();
		//instance方法查询是否存在本对象,否则将本对象存入
        messages = JavacMessages.instance(context);
        messages.add(Main.javacBundleName);
		//instance方法查询是否存在本对象,否则将本对象存入
        final Options options = Options.instance(context);
        //对命令行表进行初始化设置
        initOptions(options);
        //将命令行表设置的方法作为线程接口Runnable的run方法存入自身数组中
        options.addListener(() -> initOptions(options));
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值