精华内容
下载资源
问答
  • JAVAC源码 LR分析法 以及一个JAVA的词法分析 ,肯定不会后悔的
  • Javac源码简单分析之Javac简单介绍

    千次阅读 2014-05-22 10:53:49
    一、简单介绍 javac 是java语言编程编译器。javac工具读由java语言编写的类和接口的定义,并将它们编译成字节代码的class文件。...Javac源码就在OpenJDK源码里面。 或者在CSDN下载:http://download.csdn.net/det

    一、简单介绍

    javac 是java语言编程编译器。javac工具读由java语言编写的类和接口的定义,并将它们编译成字节代码的class文件。


    二、源码获取

    OpenJDK6源码: http://download.java.net/openjdk/jdk6/
    Javac的源码就在OpenJDK源码里面。
    或者在CSDN下载: http://download.csdn.net/detail/p_3er/7383741


    三、Javac的包

    Javac的公共入口点是com.sun.tools.javac.main.Main。在com.sun.tools.javac下面的包的组织如下:


    com.sun.tools.javac.code
    描述java语言内在语义的类 – 类型types, 符号symbols,等.


    com.sun.tools.javac.comp
    用语义细节来分析和标注语法分析树, 比如确定标识符identifiers的类型和符号。


    com.sun.tools.javac.jvm
    用于读写class files的后端类.


    com.sun.tools.javac.main
    顶层的驱动类. 编译器的标准入口点是 com.sun.tools.javac.main.Main (more...)


    com.sun.tools.javac.parser
    读取java源文件并创建语法分析树的类


    com.sun.tools.javac.resources
    编译器产生的资源文件. 其中两个是由"属性文件编译器"从属性源文件中生成的。Compiler.properties and javac.properties; 第三个是在构建的时候自动产生的,保存版本信息.version.properties


    com.sun.tools.javac.tree
    表示java语言的被标注的语法树的类. 最顶层的节点Tree.TopLevel表示源文件的内容(应该是JCTree.TopLevel)


    com.sun.tools.javac.util
    工具类, 提供调试、文件系统存取和javac的集合类的支持.
    展开全文
  • 符号表中所登记的信息在编译的不同阶段都要用到,在语义分析(后面的步骤)中,符号表所登记的内容将用于语义检查和产生中间代码,在目标代码生成阶段,党对符号名进行地址分配时,符号表是地址分配的依据。...

    一、说明

    符号表是由一组符号地址和符号信息构成的表格。符号表中所登记的信息在编译的不同阶段都要用到,在语义分析(后面的步骤)中,符号表所登记的内容将用于语义检查和产生中间代码,在目标代码生成阶段,党对符号名进行地址分配时,符号表是地址分配的依据。


    二、主要的类与方法

    解析和填充符号表这个过程主要由com.sun.tools.javac.comp.Entry及com.sun.tools.javac.comp.MemberEnter两个类来实现的。  


       

    com.sun.tools.javac.comp.Entry 主要的方法如下:

    /**
         * 访问类声明
         */
        public void visitClassDef(JCClassDecl tree) {
            Symbol owner = env.info.scope.owner;
            Scope enclScope = enterScope(env);
            ClassSymbol c;
            if (owner.kind == PCK) {
                // We are seeing a toplevel class.
                PackageSymbol packge = (PackageSymbol)owner;
                for (Symbol q = packge; q != null && q.kind == PCK; q = q.owner)
                    q.flags_field |= EXISTS;
                c = reader.enterClass(tree.name, packge);
                packge.members().enterIfAbsent(c);
                if ((tree.mods.flags & PUBLIC) != 0 && !classNameMatchesFileName(c, env)) {
                    log.error(tree.pos(),
                              "class.public.should.be.in.file", tree.name);
                }
            } else {
                if (tree.name.len != 0 &&
                    !chk.checkUniqueClassName(tree.pos(), tree.name, enclScope)) {
                    result = null;
                    return;
                }
                if (owner.kind == TYP) {
                    // We are seeing a member class.
                    c = reader.enterClass(tree.name, (TypeSymbol)owner);
                    if ((owner.flags_field & INTERFACE) != 0) {
                        tree.mods.flags |= PUBLIC | STATIC;
                    }
                } else {
                    // We are seeing a local class.
                    c = reader.defineClass(tree.name, owner);
                    c.flatname = chk.localClassName(c);
                    if (c.name.len != 0)
                        chk.checkTransparentClass(tree.pos(), c, env.info.scope);
                }
            }
            tree.sym = c;
    
            // Enter class into `compiled' table and enclosing scope.
            if (chk.compiled.get(c.flatname) != null) {
                duplicateClass(tree.pos(), c);
                result = new ErrorType(tree.name, (TypeSymbol)owner);
                tree.sym = (ClassSymbol)result.tsym;
                return;
            }
            chk.compiled.put(c.flatname, c);
            enclScope.enter(c);
    
            // Set up an environment for class block and store in `typeEnvs'
            // table, to be retrieved later in memberEnter and attribution.
            Env<AttrContext> localEnv = classEnv(tree, env);
            typeEnvs.put(c, localEnv);
    
            // Fill out class fields.
            c.completer = memberEnter;
            c.flags_field = chk.checkFlags(tree.pos(), tree.mods.flags, c, tree);
            c.sourcefile = env.toplevel.sourcefile;
            c.members_field = new Scope(c);
    
            ClassType ct = (ClassType)c.type;
            if (owner.kind != PCK && (c.flags_field & STATIC) == 0) {
                // We are seeing a local or inner class.
                // Set outer_field of this class to closest enclosing class
                // which contains this class in a non-static context
                // (its "enclosing instance class"), provided such a class exists.
                Symbol owner1 = owner;
                while ((owner1.kind & (VAR | MTH)) != 0 &&
                       (owner1.flags_field & STATIC) == 0) {
                    owner1 = owner1.owner;
                }
                if (owner1.kind == TYP) {
                    ct.setEnclosingType(owner1.type);
                }
            }
    
            // Enter type parameters.
            ct.typarams_field = classEnter(tree.typarams, localEnv);
    
            // Add non-local class to uncompleted, to make sure it will be
            // completed later.
            if (!c.isLocal() && uncompleted != null) uncompleted.append(c);
    //      System.err.println("entering " + c.fullname + " in " + c.owner);//DEBUG
    
            // Recursively enter all member classes.
            classEnter(tree.defs, localEnv);
    
            result = c.type;
        }
    	
    	 /** Main method: enter all classes in a list of toplevel trees.
         *  @param trees      The list of trees to be processed.
         */
        public void main(List<JCCompilationUnit> trees) {
            complete(trees, null);
        }
    
        /**
         * Main method: enter one class from a list of toplevel trees and
         *  place the rest on uncompleted for later processing.
         *  @param trees      The list of trees to be processed.
         *  @param c          The class symbol to be processed.
         */
        public void complete(List<JCCompilationUnit> trees, ClassSymbol c) {
            annotate.enterStart();
            ListBuffer<ClassSymbol> prevUncompleted = uncompleted;
            if (memberEnter.completionEnabled) uncompleted = new ListBuffer<ClassSymbol>();
    
            try {
                // enter all classes, and construct uncompleted list
                classEnter(trees, null);
    
                // complete all uncompleted classes in memberEnter
                if  (memberEnter.completionEnabled) {
                    while (uncompleted.nonEmpty()) {
                        ClassSymbol clazz = uncompleted.next();
                        if (c == null || c == clazz || prevUncompleted == null)
                            clazz.complete();
                        else
                            //将类符号放入prevUncompleted列表(uncompleted列表)
                            prevUncompleted.append(clazz);
                    }
    
                    // if there remain any unimported toplevels (these must have
                    // no classes at all), process their import statements as well.
                    /**
                     * uncompleted列表没有的符号(除类符号外),根据improt声明,给顶级抽象树都添加了一个MemberEnter对象
                     * 这些符号(包括类的参数类型符号也就是泛型、父类符号、接口类型符等)
                     */
                    for (JCCompilationUnit tree : trees) {
                        if (tree.starImportScope.elems == null) {
                            JavaFileObject prev = log.useSource(tree.sourcefile);
                            Env<AttrContext> env = typeEnvs.get(tree);
                            if (env == null)
                                env = topLevelEnv(tree);
                            memberEnter.memberEnter(tree, env);
                            log.useSource(prev);
                        }
                    }
                }
            } finally {
            	//prevUncompleted列表赋值给uncompleted列表
                uncompleted = prevUncompleted;
                annotate.enterDone();
            }
        }
     


    com.sun.tools.javac.comp.MemberEnter 主要的方法如下:

    /** Complete entering a class.
         *  将未处理列表中的所有符号都解析到各自的类符号表中
         *  @param sym         The symbol of the class to be completed.
         */
        public void complete(Symbol sym) throws CompletionFailure {
            // Suppress some (recursive) MemberEnter invocations
            if (!completionEnabled) {
                // Re-install same completer for next time around and return.
                assert (sym.flags() & Flags.COMPOUND) == 0;
                sym.completer = this;
                return;
            }
    
            ClassSymbol c = (ClassSymbol)sym;
            ClassType ct = (ClassType)c.type;
            Env<AttrContext> env = enter.typeEnvs.get(c);
            JCClassDecl tree = (JCClassDecl)env.tree;
            boolean wasFirst = isFirst;
            isFirst = false;
    
            JavaFileObject prev = log.useSource(env.toplevel.sourcefile);
            try {
                // Save class environment for later member enter (2) processing.
                halfcompleted.append(env);
    
                // If this is a toplevel-class, make sure any preceding import
                // clauses have been seen.
                if (c.owner.kind == PCK) {
                    memberEnter(env.toplevel, env.enclosing(JCTree.TOPLEVEL));
                    todo.append(env);
                }
    
                // Mark class as not yet attributed.
                c.flags_field |= UNATTRIBUTED;
    
                if (c.owner.kind == TYP)
                    c.owner.complete();
    
                // create an environment for evaluating the base clauses
                Env<AttrContext> baseEnv = baseEnv(tree, env);
    
                // Determine supertype.
                Type supertype =
                    (tree.extending != null)
                    ? attr.attribBase(tree.extending, baseEnv, true, false, true)
                    : ((tree.mods.flags & Flags.ENUM) != 0 && !target.compilerBootstrap(c))
                    ? attr.attribBase(enumBase(tree.pos, c), baseEnv,
                                      true, false, false)
                    : (c.fullname == names.java_lang_Object)
                    ? Type.noType
                    : syms.objectType;
                ct.supertype_field = supertype;
    
                // Determine interfaces.
                ListBuffer<Type> interfaces = new ListBuffer<Type>();
                Set<Type> interfaceSet = new HashSet<Type>();
                List<JCExpression> interfaceTrees = tree.implementing;
                if ((tree.mods.flags & Flags.ENUM) != 0 && target.compilerBootstrap(c)) {
                    // add interface Comparable<T>
                    interfaceTrees =
                        interfaceTrees.prepend(make.Type(new ClassType(syms.comparableType.getEnclosingType(),
                                                                       List.of(c.type),
                                                                       syms.comparableType.tsym)));
                    // add interface Serializable
                    interfaceTrees =
                        interfaceTrees.prepend(make.Type(syms.serializableType));
                }
                for (JCExpression iface : interfaceTrees) {
                    Type i = attr.attribBase(iface, baseEnv, false, true, true);
                    if (i.tag == CLASS) {
                        interfaces.append(i);
                        chk.checkNotRepeated(iface.pos(), types.erasure(i), interfaceSet);
                    }
                }
                if ((c.flags_field & ANNOTATION) != 0)
                    ct.interfaces_field = List.of(syms.annotationType);
                else
                    ct.interfaces_field = interfaces.toList();
    
                if (c.fullname == names.java_lang_Object) {
                    if (tree.extending != null) {
                        chk.checkNonCyclic(tree.extending.pos(),
                                           supertype);
                        ct.supertype_field = Type.noType;
                    }
                    else if (tree.implementing.nonEmpty()) {
                        chk.checkNonCyclic(tree.implementing.head.pos(),
                                           ct.interfaces_field.head);
                        ct.interfaces_field = List.nil();
                    }
                }
    
                // Annotations.
                // In general, we cannot fully process annotations yet,  but we
                // can attribute the annotation types and then check to see if the
                // @Deprecated annotation is present.
                attr.attribAnnotationTypes(tree.mods.annotations, baseEnv);
                if (hasDeprecatedAnnotation(tree.mods.annotations))
                    c.flags_field |= DEPRECATED;
                annotateLater(tree.mods.annotations, baseEnv, c);
    
                attr.attribTypeVariables(tree.typarams, baseEnv);
    
                chk.checkNonCyclic(tree.pos(), c.type);
    
                /**
                 * 增加一个默认的构造方法(当类没有构造方法时)
                 */
                if ((c.flags() & INTERFACE) == 0 &&
                    !TreeInfo.hasConstructors(tree.defs)) {
                    List<Type> argtypes = List.nil();
                    List<Type> typarams = List.nil();
                    List<Type> thrown = List.nil();
                    long ctorFlags = 0;
                    boolean based = false;
                    if (c.name.len == 0) {
                        JCNewClass nc = (JCNewClass)env.next.tree;
                        if (nc.constructor != null) {
                            Type superConstrType = types.memberType(c.type,
                                                                    nc.constructor);
                            argtypes = superConstrType.getParameterTypes();
                            typarams = superConstrType.getTypeArguments();
                            ctorFlags = nc.constructor.flags() & VARARGS;
                            if (nc.encl != null) {
                                argtypes = argtypes.prepend(nc.encl.type);
                                based = true;
                            }
                            thrown = superConstrType.getThrownTypes();
                        }
                    }
                    JCTree constrDef = DefaultConstructor(make.at(tree.pos), c,
                                                        typarams, argtypes, thrown,
                                                        ctorFlags, based);
                    tree.defs = tree.defs.prepend(constrDef);
                }
    
                // If this is a class, enter symbols for this and super into
                // current scope.
                if ((c.flags_field & INTERFACE) == 0) {
                    VarSymbol thisSym =
                        new VarSymbol(FINAL | HASINIT, names._this, c.type, c);
                    thisSym.pos = Position.FIRSTPOS;
                    env.info.scope.enter(thisSym);
                    if (ct.supertype_field.tag == CLASS) {
                        VarSymbol superSym =
                            new VarSymbol(FINAL | HASINIT, names._super,
                                          ct.supertype_field, c);
                        superSym.pos = Position.FIRSTPOS;
                        env.info.scope.enter(superSym);
                    }
                }
    
                // check that no package exists with same fully qualified name,
                // but admit classes in the unnamed package which have the same
                // name as a top-level package.
                if (checkClash &&
                    c.owner.kind == PCK && c.owner != syms.unnamedPackage &&
                    reader.packageExists(c.fullname))
                    {
                        log.error(tree.pos, "clash.with.pkg.of.same.name", c);
                    }
    
            } catch (CompletionFailure ex) {
                chk.completionError(tree.pos(), ex);
            } finally {
                log.useSource(prev);
            }
    
            // Enter all member fields and methods of a set of half completed
            // classes in a second phase.
            if (wasFirst) {
                try {
                    while (halfcompleted.nonEmpty()) {
                        finish(halfcompleted.next());
                    }
                } finally {
                    isFirst = true;
                }
    
                // commit pending annotations
                annotate.flush();
            }
        }


    三、过程及简单源码解析

    Enter过程中,编译器会找到当前范围(enclosing scope)中发现的所有的定义(definitions),并且把这些定义注册成符号(symbols)。
    Enter又分为以下两个阶段:


    第一个阶段:



    编译器会注册所有类的符号,并且把这写符号和相应的范围(scope)联系在一起。实现方法是使用一个Visitor(访问者)类,由上而下的遍历AST(抽象语法树),访问所有的类,包括类里面的内部类。Enter给每一个类的符号都添加了一个MemberEnter对象,这个对象是由第二个阶段来调用的。
     

    整个操作的方法调用过程如下:




    上面这个过程是访问者模式的一种实现。
    Enter是一个JCTree.Visitor.Enter.classEnter(l.head, env)调用JCTree.accept(Visitor v),而accept方法又是调用的Visitor类里面的visitXXX()方法,而这些方法的实现又是在Enter类中。也就是Enter.visitClassDef(JCClassDecl tree)方法,在这个方法中,会将类符号放入uncompleted列表; 


    visitClassDef(JCClassDecl tree)方法主要做三件事:

    1、将类符号(当前类)填入类自身的符号表,添加了一个MemberEnter对象
           

            // Enter class into `compiled' table and enclosing scope.
            if (chk.compiled.get(c.flatname) != null) {
                duplicateClass(tree.pos(), c);
                result = new ErrorType(tree.name, (TypeSymbol)owner);
                tree.sym = (ClassSymbol)result.tsym;
                return;
            }
            chk.compiled.put(c.flatname, c);
            enclScope.enter(c);
    		
    		// Set up an environment for class block and store in `typeEnvs'
            // table, to be retrieved later in memberEnter and attribution.
            Env<AttrContext> localEnv = classEnv(tree, env);
            typeEnvs.put(c, localEnv);
    
    
            // Fill out class fields.
            c.completer = memberEnter;
            c.flags_field = chk.checkFlags(tree.pos(), tree.mods.flags, c, tree);
            c.sourcefile = env.toplevel.sourcefile;
            c.members_field = new Scope(c);



    2、解析填写其它的类符号,包括当前类中使用到的内部类、枚举、变量等抽象树的类符号。
            // Enter type parameters.
            ct.typarams_field = classEnter(tree.typarams, localEnv);




    3、将类符号放入uncompleted列表
            
    if (!c.isLocal() && uncompleted != null) uncompleted.append(c);





    Enter给每一个类的符号都添加了一个MemberEnter对象,这个对象是由第二个阶段来调用的。
    memberEnter.memberEnter(tree, env);	

    第二个阶段:



    这些类被MemberEnter对象所完成(completed,即完成类的成员变量的Enter)。首先,MemberEnter决定一个类的参数,父类和接口。然后这些符号被添加进了类的范围中。不像前一个步骤,这个步骤是懒惰执行的。类的成员只有在被访问时,才加入类的定义中的。这里的实现,是通过安装一个完成对象(member object)到类的符号中。这些对象可以在需要时调用memberEnter。



    整个操作的方法调用过程如下:



    Enter是一个JCTree.Visitor.Enter.classEnter(l.head, env)调用JCTree.accept(Visitor v),而accept方法又是调用的Visitor类里面的visitXXX()方法,而这些方法的实现又是在Enter类中。也就是Enter.visitClassDef(JCClassDecl tree)方法,在这个方法中,会将类符号解析和填入类自身的符号表。




    最后,enter把所有的顶层类(top-level classes)放到一个todo-queue中。


    展开全文
  • Java-底层原理-javac源码笔记

    千次阅读 2018-12-23 21:35:29
    Java-JVM-javac源码笔记 摘要 本文只是简单记录下javac的源码阅读笔记 未完待续 0x01 简介 Java的编译有三类: 1.1 前端编译器 简介 如Javac,此类前端编译器的优化主要是针对Java编码过程 功能 将.java转为.class ...

    Java-底层原理-javac源码笔记

    系列文章目录

    Java-底层原理-编译原理
    Java-底层原理-javac源码笔记
    Java-底层原理-类加载机制
    Java-底层原理-clinit和init

    摘要

    本文只是简单记录下javac的源码阅读笔记

    未完待续

    0x01 简介

    1.1 解释执行和编译执行

    可以参考文章Java-JVM-编译原理
    Java程序一般是将.java文件编译为.class文件,然后再运行时由JVM的解释器(如templateInterpreter_x86_64.cpp,bytecodeInterpreter_x86.cpp等)解释运行字节码文件。

    • 本地代码
      指适合当前计算机运行的指令集

    解释执行和编译执行:

    • 解释执行
      逐条解释执行源语言,如phpjavascript就是典型的解释性语言。具体到java,就是用解释器直接解释执行基于栈的字节码指令集;
    • 编译执行
      将源语言代码编译为目标代码,执行时不再需要编译,而是直接在支持目标代码的平台上运行,效率更高。具体到java,指以方法为基本单位,将字节码翻译为本地机器码后再执行。

    HotSpot的解释执行和即时编译:

    • 解释执行
      将已编译的字节码文件逐行转换为本地代码,并使用解释器执行之。每次运行同样代码也需要反复转换字节码到本地代码过程。
      优点:不用等待
    • 即时编译(Just In Time Compile, JIT编译)
      指以方法为基本单位,将字节码翻译为本地代码后再执行。也就是说说,初次执行时速度慢,以后执行可以直接执行本地代码,速度快
      优点:总的来说效率更高

    HotSpot的两种运行模式有不同的规定:

    • -server模式:先解释字节码文件后执行。且有JIT即时编译器,会将JVM统计的执行热点代码的字节码文件编译为本地机器代码,提升执行效率。当热点不再时,就会释放这些本地代码,待执行时重新解释执行。
    • -client模式:逐条解释执行字节码文件。

    Java的编译有三类:

    1.1 前端编译器

    • 简介
      Javac,此类前端编译器的优化主要是针对Java编码过程
    • 功能
      .java转为.class
    • 主流实现
      Javac

    1.2 JIT编译器(后端编译器)

    • 简介
      Just in time,即时编译器。可以把热点代码直接转为机器码,提升效率。同时也是主要优化方向。对于程序运行表现至关重要。
    • 功能
      .class转为机器码
    • 主流实现
      HotSpot之C1(Client编译器,优化手法简单,编译时间短。针对启动性能有要求的客户端GUI程序),C2(Server编译器,优化手段复杂,编译时间较长,运行总体性能更好。针对心梗峰值)。且在JDK1.7后,Server模式JVM采用分层编译为默认编译策略,会根据编译器编译、优化的规模和耗时,划分不同的编译层次:
      1.0层:程序直接解释执行,
      2.1层,即C1编译。将字节码编译为本地机器代码,

    1.3 AOT编译器

    • 简介
      Ahead of time,静态提前编译器
    • 功能
      .java转为机器码
    • 主流实现
      GNU Compiler for the Java(GCJ)

    0x02 Javac源码

    这里调试使用的是openjdk8javac代码在jdk8/langtools/src/share/classes/com/sun/tools/javac之中。

    main方法位于com/sun/tools/javac/Main.java:

    public static void main(String[] args) throws Exception {
        System.exit(compile(args));
    }
    

    然后是到com/sun/tools/javac/main/Main.java的以下方法:

    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) {
            // A fresh context was created above, so jfm must be a JavacFileManager
            ((JavacFileManager)fileManager).close();
        }
        return result;
    }
    

    后序会达到这个Main类的下面这个方法,核心代码如下:

     public Result compile(String[] args,
                              String[] classNames,
                              Context context,
                              List<JavaFileObject> fileObjects,
                              Iterable<? extends Processor> processors)
    {
         // 检测到语法不对,就返回代表错误的码Result.CMDERR
        if (args.length == 0
                        && (classNames == null || classNames.length == 0)
                        && fileObjects.isEmpty()) {
                    Option.HELP.process(optionHelper, "-help");
                    return Result.CMDERR;
                }
        Collection<File> files;    
        // 得到要命令行中要编译的文件集合   
        files = processArgs(CommandLine.parse(args), classNames);
        fileManager = context.get(JavaFileManager.class);
    
        if (!files.isEmpty()) {
            // add filenames to fileObjects
            comp = JavaCompiler.instance(context);
            List<JavaFileObject> otherFiles = List.nil();
            JavacFileManager dfm = (JavacFileManager)fileManager;
            for (JavaFileObject fo : dfm.getJavaFileObjectsFromFiles(files))
                otherFiles = otherFiles.prepend(fo);
            for (JavaFileObject fo : otherFiles)
                fileObjects = fileObjects.prepend(fo);
        }
        JavaCompiler comp = null;
        
        comp = JavaCompiler.instance(context);
        
        comp.compile(fileObjects,
                  classnames.toList(),
                  processors);
    }
    

    接下来,会走入关键类com/sun/tools/javac/main/JavaCompiler.java,方法主要看compilecompile2:

    public void compile(List<JavaFileObject> sourceFileObjects,
                            List<String> classnames,
                            Iterable<? extends Processor> processors)
    {
    // 初始化插入式注解处理器
    initProcessAnnotations(processors);
    
                // These method calls must be chained to avoid memory leaks
                delegateCompiler =
                    // 2.注解处理执行
                    processAnnotations(
                        // 1.1 parseFiles:词法分析和语法分析
                        // 1.2 输入到符号表
                        enterTrees(stopIfError(CompileState.PARSE, parseFiles(sourceFileObjects))),
                        classnames);
                // 3.分析及字节码class文件生成
                delegateCompiler.compile2();
    }
    

    0x03 Javac编译过程

    主要分为
    解析与填充符号表 -> 注解处理 -> 分析与字节码生成

    3.1 解析与填充符号表

    就是前面提到过的parseFiles,解析语法树的过程使用的是javac自己的一套,可以参考这篇文章JCTree语法树结点类型

    public List<JCCompilationUnit> parseFiles(Iterable<JavaFileObject> fileObjects) {
       if (shouldStop(CompileState.PARSE))
           return List.nil();
    
        // 语法树对象
        ListBuffer<JCCompilationUnit> trees = new ListBuffer<>();
        Set<JavaFileObject> filesSoFar = new HashSet<JavaFileObject>();
        
        for (JavaFileObject fileObject : fileObjects) {
            if (!filesSoFar.contains(fileObject)) {
                filesSoFar.add(fileObject);
                trees.append(parse(fileObject));
            }
        }
        return trees.toList();
    }
    

    这里主要会进行词法和语法树解析:

    1. 词法分析,CharStream拆分为tokens
    2. 语法分析,将tokens构建为抽象语法树(AST)。其每个节点代表一个语法结构,如包、运算符等。

    语法分析使用的是com.sun.tools.javac.parser.JavacParser

    0xFF

    F.1 好文推荐

    Java虚拟机进阶之一:Java VM前世今生

    javac、java打jar包命令实例

    Javac源码简单分析之解析和填充符号表

    JAVAC原理

    javac 编译器原理

    为啥要看javac源代码

    展开全文
  • javac源码详解openJDKSE8版本4DataFlow源码详解DataFlow概述 DataFlow概述 数据流分析:主要有4种数据流分析。分别是: AliveAnalyzer 活性分析; AssignAnalyzer 初始化赋值分析包括定值再次被赋值; FlowAnalyzer...

    DataFlow概述

    1.Flow: 数据流分析的主要类,基本包含了所有数据流分析的代码
    2.TreeScanner: 遍历抽象语法树,根据不同的语法树进行不同的数据流分析
    3. BaseAnalyzer: 提供4大数据流分析的基本方法,同时也是主要处理Jumps(break;continue;return;throw)的地方
    4. AliveAnalyzer: 活性分析,检查所有的语句是否都有机会被执行。重要的属性alive标识当前抽象语法树是否可以有机会被执行。
    5. AssignAnalyzer: 赋值分析,变量初始化分析,检查变量再使用时是否已经被赋初值,检查final变量是否被二次赋值。
    6. AbstractAssignAnalyzer: 抽象赋值分析,变量初始化分析,检查变量再使用时是否已经被赋初值,检查final变量是否被二次赋值。常用的属性有:inits(被赋值的变量的集合)。uninits(未被赋值 的变量的集合)。uninitsTry(try block中没有赋值的变量的集合)。initsWhenTrue(控制条件为真时,被赋值的变量的集合)。initsWhenFalse(控制条件为假时,被赋值的变量的集合)。uninitsWhenTrue(控制条件为真时,未被赋值的变量的集合)。uninitsWhenFalse(控制条件为假时,未被赋值的变量的集合)。vardecls(被定义的变量的数组)。classDef(当前被定义的类)。firstadr(当前被定义的类的第一个变量的再被定义的变量的数组中的下标)。nextadr(当前被定义的类的下一个变量的再被定义的变量的数组中的下标)。returnadr(当前被定义的block的第一个可以return的变量的再被定义的变量的数组中的下标)。syms(symbolTable)。names(nameTable)
    7. FlowAnalyzer: 异常分析,主要检测变量checked异常是否都被处理。主要的属性有:preciseRethrowTypes(标识最后一行代码是否可以被正确执行)。thrown(抛出的checked异常)。caught(被捕获的checked异常)。
    8. CaptureAnalyzer: 主要检测final变量的二次赋值。
    9. PendingExit: 主要用来保存continue,break,return,exception这四种具有jump能力的语句
    10. AbstractAssignPendingExit:主要用来保存退出block时,已经被初始化的变量和未被初始化的变量还有一开始进入block时的初始化变量和为被初始化的变量。主要的属性为:inits,uninits,exit_inits,exit_uninits
    11. Bits:用来控制对变量状态的变化,以及对inits,uninits集合的操作。
    11.讲解的安排: 这里会粗略讲解 AliveAnalyzer(这里非常简单),着重讲解AssignAnalyzer(编译原理中多次以此为例子,刚好可以拿来印证下),不讲解FlowAnalyzer(感兴趣的可以自行阅读源码)和FlowAnalyzer(这个是做为AssignAnalyzer的一种特列)。

    入口

    从com.sun.tools.javac.main.JavaCompiler的863行

                delegateCompiler.compile2();
                delegateCompiler.close();
                elapsed_msec = delegateCompiler.elapsed_msec;
    

    一直点进来到com.sun.tools.javac.comp.Flow的208行

        public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
            new AliveAnalyzer().analyzeTree(env, make);
            new AssignAnalyzer(log, syms, lint, names).analyzeTree(env);
            new FlowAnalyzer().analyzeTree(env, make);
            new CaptureAnalyzer().analyzeTree(env, make);
        }
    

    AliveAnalyzer概述

    这里的检查是相对比较简单的:主要针对如:if条件始终为false,while条件始终为false,return后面有语句,break后面有语句。

    AliveAnalyzer入口

    老样子,使用观察者模式进行,遍历抽象语法树

        /**************************************************************************
         * main method
         *************************************************************************/
    
            /** Perform definite assignment/unassignment analysis on a tree.
             */
            public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
                analyzeTree(env, env.tree, make);
            }
            public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) {
                try {
                    attrEnv = env;
                    Flow.this.make = make;
                    pendingExits = new ListBuffer<PendingExit>();
                    alive = true;
                    scan(tree);
                } finally {
                    pendingExits = null;
                    Flow.this.make = null;
                }
            }
    

    AliveAnalyzer 处理class

    经过attribute环节,仅需要处理静态初始化方法,实例初始化方法和所有的方法即可

            /* ------------ Visitor methods for various sorts of trees -------------*/
    
            public void visitClassDef(JCClassDecl tree) {
                if (tree.sym == null) return;
                boolean alivePrev = alive;
                ListBuffer<PendingExit> pendingExitsPrev = pendingExits;
                Lint lintPrev = lint;
    
                pendingExits = new ListBuffer<PendingExit>();
                lint = lint.augment(tree.sym);
    
                try {
                    // process all the static initializers
                    for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
                        if (!l.head.hasTag(METHODDEF) &&
                            (TreeInfo.flags(l.head) & STATIC) != 0) {
                            scanDef(l.head);
                        }
                    }
    
                    // process all the instance initializers
                    for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
                        if (!l.head.hasTag(METHODDEF) &&
                            (TreeInfo.flags(l.head) & STATIC) == 0) {
                            scanDef(l.head);
                        }
                    }
    
                    // process all the methods
                    for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
                        if (l.head.hasTag(METHODDEF)) {
                            scan(l.head);
                        }
                    }
                } finally {
                    pendingExits = pendingExitsPrev;
                    alive = alivePrev;
                    lint = lintPrev;
                }
            }
    

    AliveAnalyzer 处理method

    处理方法内的语句,如果有不可达的语句,则使用日志记录下来

            public void visitMethodDef(JCMethodDecl tree) {
                if (tree.body == null) return;
                Lint lintPrev = lint;
    
                lint = lint.augment(tree.sym);
    
                Assert.check(pendingExits.isEmpty());
    
                try {
                    alive = true;
                    scanStat(tree.body);
    
                    if (alive && !tree.sym.type.getReturnType().hasTag(VOID))
                        log.error(TreeInfo.diagEndPos(tree.body), "missing.ret.stmt");
    
                    List<PendingExit> exits = pendingExits.toList();
                    pendingExits = new ListBuffer<PendingExit>();
                    while (exits.nonEmpty()) {
                        PendingExit exit = exits.head;
                        exits = exits.tail;
                        Assert.check(exit.tree.hasTag(RETURN));
                    }
                } finally {
                    lint = lintPrev;
                }
            }
    

    AliveAnalyzer 典例while

    这里主要以while语句来说明,校验的过程。
    prevPendingExits 保存着之前的jumps。
    先处理条件,如果条件始终为false,那么就会标记当前while语句body部分不可达,并在遍历body时打印,不可达信息。
    接下来处理,内部的语句,如果内部有语句不可达,则alive变为false。
    然后处理所有的continue语句。
    然后处理所有的break语句。

            public void visitWhileLoop(JCWhileLoop tree) {
                ListBuffer<PendingExit> prevPendingExits = pendingExits;
                pendingExits = new ListBuffer<PendingExit>();
                scan(tree.cond);
                alive = !tree.cond.type.isFalse();
                scanStat(tree.body);
                alive |= resolveContinues(tree);
                alive = resolveBreaks(tree, prevPendingExits) ||
                    !tree.cond.type.isTrue();
            }
    

    body错误的打印

            /** Analyze a statement. Check that statement is reachable.
             */
            void scanStat(JCTree tree) {
                if (!alive && tree != null) {
                    log.error(tree.pos(), "unreachable.stmt");
                    if (!tree.hasTag(SKIP)) alive = true;
                }
                scan(tree);
            }
    

    AssignAnalyzer概述

    遍历抽象语法树,通过inits,uninits来处理没有分支的变量的已被复制和未被赋值问题。通过initsWhenTrue,initsWhenFalse,uninitsWhenTrue,uninitsWhenFalse来处理有分支的变量的已被复制和未被赋值问题。而这些基本都是使用二进制表示的,相关的运算也是使用二进制运算。

    AssignAnalyzer入口

    老样子,使用观察者模式进行,遍历抽象语法树。并对必要的对象赋初值。

        /**************************************************************************
         * main method
         *************************************************************************/
    
            /** Perform definite assignment/unassignment analysis on a tree.
             */
            public void analyzeTree(Env<?> env) {
                analyzeTree(env, env.tree);
             }
    
            public void analyzeTree(Env<?> env, JCTree tree) {
                try {
                    startPos = tree.pos().getStartPosition();
    
                    if (vardecls == null)
                        vardecls = new JCVariableDecl[32];
                    else
                        for (int i=0; i<vardecls.length; i++)
                            vardecls[i] = null;
                    firstadr = 0;
                    nextadr = 0;
                    pendingExits = new ListBuffer<>();
                    this.classDef = null;
                    unrefdResources = new Scope(env.enclClass.sym);
                    scan(tree);
                } finally {
                    // note that recursive invocations of this method fail hard
                    startPos = -1;
                    resetBits(inits, uninits, uninitsTry, initsWhenTrue,
                            initsWhenFalse, uninitsWhenTrue, uninitsWhenFalse);
                    if (vardecls != null) {
                        for (int i=0; i<vardecls.length; i++)
                            vardecls[i] = null;
                    }
                    firstadr = 0;
                    nextadr = 0;
                    pendingExits = null;
                    this.classDef = null;
                    unrefdResources = null;
                }
            }
        }
    

    AssignAnalyzer 处理class

    经过attribute环节,这里先处理静态字段,后处理静态初始化方法,后处理,实例字段,后处理实例初始化方法,最后处理所有方法。
    这里处理静态字段和实例字段的还有和方法内的变量具有相似之处。

            /* ------------ Visitor methods for various sorts of trees -------------*/
    
            @Override
            public void visitClassDef(JCClassDecl tree) {
                if (tree.sym == null) {
                    return;
                }
    
                JCClassDecl classDefPrev = classDef;
                int firstadrPrev = firstadr;
                int nextadrPrev = nextadr;
                ListBuffer<P> pendingExitsPrev = pendingExits;
    
                pendingExits = new ListBuffer<P>();
                if (tree.name != names.empty) {
                    firstadr = nextadr;
                }
                classDef = tree;
                try {
                    // define all the static fields
                    for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
                        if (l.head.hasTag(VARDEF)) {
                            JCVariableDecl def = (JCVariableDecl)l.head;
                            if ((def.mods.flags & STATIC) != 0) {
                                VarSymbol sym = def.sym;
                                if (trackable(sym)) {
                                    newVar(def);
                                }
                            }
                        }
                    }
    
                    // process all the static initializers
                    for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
                        if (!l.head.hasTag(METHODDEF) &&
                            (TreeInfo.flags(l.head) & STATIC) != 0) {
                            scan(l.head);
                        }
                    }
    
                    // define all the instance fields
                    for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
                        if (l.head.hasTag(VARDEF)) {
                            JCVariableDecl def = (JCVariableDecl)l.head;
                            if ((def.mods.flags & STATIC) == 0) {
                                VarSymbol sym = def.sym;
                                if (trackable(sym)) {
                                    newVar(def);
                                }
                            }
                        }
                    }
    
                    // process all the instance initializers
                    for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
                        if (!l.head.hasTag(METHODDEF) &&
                            (TreeInfo.flags(l.head) & STATIC) == 0) {
                            scan(l.head);
                        }
                    }
    
                    // process all the methods
                    for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
                        if (l.head.hasTag(METHODDEF)) {
                            scan(l.head);
                        }
                    }
                } finally {
                    pendingExits = pendingExitsPrev;
                    nextadr = nextadrPrev;
                    firstadr = firstadrPrev;
                    classDef = classDefPrev;
                }
            }
    

    1.静态字段的处理
    在class的成员中 找到所有被static修饰的字段, 判断,是否可以被追踪。如果是,则生成生成变量。

                    // define all the static fields
                    for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
                        if (l.head.hasTag(VARDEF)) {
                            JCVariableDecl def = (JCVariableDecl)l.head;
                            if ((def.mods.flags & STATIC) != 0) {
                                VarSymbol sym = def.sym;
                                if (trackable(sym)) {
                                    newVar(def);
                                }
                            }
                        }
                    }
    

    是否可以被追踪:1.symbol的位置大于等于当前的起点symbol的位置
    2 symol属于方法 或者 (symol是被final修饰,且不能是参数,且symol定义时未带初始化语句,且symbol再当前定义的方法中)

            /** Do we need to track init/uninit state of this symbol?
             *  I.e. is symbol either a local or a blank final variable?
             */
            protected boolean trackable(VarSymbol sym) {
                return
                    sym.pos >= startPos &&
                    ((sym.owner.kind == MTH ||
                     ((sym.flags() & (FINAL | HASINIT | PARAMETER)) == FINAL &&
                      classDef.sym.isEnclosedBy((ClassSymbol)sym.owner))));
            }
    

    生成边变量的定义:当需要进行跟踪时。把该变量的定义放入到vardecls数组中,并把该位置放入到uninits集合中,同时调整nextadr,表示下一个可以放置变量定义的位置。

            /** Initialize new trackable variable by setting its address field
             *  to the next available sequence number and entering it under that
             *  index into the vars array.
             */
            void newVar(JCVariableDecl varDecl) {
                VarSymbol sym = varDecl.sym;
                vardecls = ArrayUtils.ensureCapacity(vardecls, nextadr);
                if ((sym.flags() & FINAL) == 0) {
                    sym.flags_field |= EFFECTIVELY_FINAL;
                }
                sym.adr = nextadr;
                vardecls[nextadr] = varDecl;
                exclVarFromInits(varDecl, nextadr);
                uninits.incl(nextadr);
                nextadr++;
            }
    

    2.静态初始化代码处理
    对有static关键字修饰的代码进行处理,会用到scan方法,scan方法到时候会在处理方法的时候在去讲解

                    // process all the static initializers
                    for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
                        if (!l.head.hasTag(METHODDEF) &&
                            (TreeInfo.flags(l.head) & STATIC) != 0) {
                            scan(l.head);
                        }
                    }
    

    3.实例字段处理
    和静态处理的非常类似

                    // define all the instance fields
                    for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
                        if (l.head.hasTag(VARDEF)) {
                            JCVariableDecl def = (JCVariableDecl)l.head;
                            if ((def.mods.flags & STATIC) == 0) {
                                VarSymbol sym = def.sym;
                                if (trackable(sym)) {
                                    newVar(def);
                                }
                            }
                        }
                    }
    

    4.实例初始化代码处理
    和实例代码块处理的非常类似

                    // process all the instance initializers
                    for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
                        if (!l.head.hasTag(METHODDEF) &&
                            (TreeInfo.flags(l.head) & STATIC) == 0) {
                            scan(l.head);
                        }
                    }
    

    5.所有方法的处理
    和实例代码块处理的非常类似

                    // process all the methods
                    for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
                        if (l.head.hasTag(METHODDEF)) {
                            scan(l.head);
                        }
                    }
    

    AssignAnalyzer 处理method

    经过简单的处理之后,会直接调用父类的方法处理method
    1.保存在进入方法之前的状态(initsPrev uninitsPrev nextadr firstadr returnadr ),处理完本方法后返回之前的状态。
    2.生成参数的定义,并默认参数已经初始化。

            @Override
            public void visitMethodDef(JCMethodDecl tree) {
                if (tree.body == null) {
                    return;
                }
    
                /*  MemberEnter can generate synthetic methods ignore them
                 */
                if ((tree.sym.flags() & SYNTHETIC) != 0) {
                    return;
                }
    
                Lint lintPrev = lint;
                lint = lint.augment(tree.sym);
                try {
                    super.visitMethodDef(tree);
                } finally {
                    lint = lintPrev;
                }
            }
    
            @Override
            public void visitMethodDef(JCMethodDecl tree) {
                if (tree.body == null) {
                    return;
                }
                /*  Ignore synthetic methods, except for translated lambda methods.
                 */
                if ((tree.sym.flags() & (SYNTHETIC | LAMBDA_METHOD)) == SYNTHETIC) {
                    return;
                }
    
                final Bits initsPrev = new Bits(inits);
                final Bits uninitsPrev = new Bits(uninits);
                int nextadrPrev = nextadr;
                int firstadrPrev = firstadr;
                int returnadrPrev = returnadr;
    
                Assert.check(pendingExits.isEmpty());
    
                try {
                    boolean isInitialConstructor =
                        TreeInfo.isInitialConstructor(tree);
    
                    if (!isInitialConstructor) {
                        firstadr = nextadr;
                    }
                    for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) {
                        JCVariableDecl def = l.head;
                        scan(def);
                        Assert.check((def.sym.flags() & PARAMETER) != 0, "Method parameter without PARAMETER flag");
                        /*  If we are executing the code from Gen, then there can be
                         *  synthetic or mandated variables, ignore them.
                         */
                        initParam(def);
                    }
                    // else we are in an instance initializer block;
                    // leave caught unchanged.
                    scan(tree.body);
    
                    if (isInitialConstructor) {
                        boolean isSynthesized = (tree.sym.flags() &
                                                 GENERATEDCONSTR) != 0;
                        for (int i = firstadr; i < nextadr; i++) {
                            JCVariableDecl vardecl = vardecls[i];
                            VarSymbol var = vardecl.sym;
                            if (var.owner == classDef.sym) {
                                // choose the diagnostic position based on whether
                                // the ctor is default(synthesized) or not
                                if (isSynthesized) {
                                    checkInit(TreeInfo.diagnosticPositionFor(var, vardecl),
                                        var, "var.not.initialized.in.default.constructor");
                                } else {
                                    checkInit(TreeInfo.diagEndPos(tree.body), var);
                                }
                            }
                        }
                    }
                    List<P> exits = pendingExits.toList();
                    pendingExits = new ListBuffer<>();
                    while (exits.nonEmpty()) {
                        P exit = exits.head;
                        exits = exits.tail;
                        Assert.check(exit.tree.hasTag(RETURN), exit.tree);
                        if (isInitialConstructor) {
                            assignToInits(exit.tree, exit.exit_inits);
                            for (int i = firstadr; i < nextadr; i++) {
                                checkInit(exit.tree.pos(), vardecls[i].sym);
                            }
                        }
                    }
                } finally {
                    assignToInits(tree, initsPrev);
                    uninits.assign(uninitsPrev);
                    nextadr = nextadrPrev;
                    firstadr = firstadrPrev;
                    returnadr = returnadrPrev;
                }
            }
    

    遍历形参

            public void visitVarDef(JCVariableDecl tree) {
                boolean track = trackable(tree.sym);
                if (track && tree.sym.owner.kind == MTH) {
                    newVar(tree);
                }
                if (tree.init != null) {
                    scanExpr(tree.init);
                    if (track) {
                        letInit(tree.pos(), tree.sym);
                    }
                }
            }
    

    遍历方法体

                    // else we are in an instance initializer block;
                    // leave caught unchanged.
                    scan(tree.body);
    

    遍历方法体
    pendingExits是非常重要的,它里记录着含着,不同语句退出方法体内的时,初始化变量和未初始化变量,以及进来时的初始化变量和未初始化变量。

                    List<P> exits = pendingExits.toList();
                    pendingExits = new ListBuffer<>();
                    while (exits.nonEmpty()) {
                        P exit = exits.head;
                        exits = exits.tail;
                        Assert.check(exit.tree.hasTag(RETURN), exit.tree);
                        if (isInitialConstructor) {
                            assignToInits(exit.tree, exit.exit_inits);
                            for (int i = firstadr; i < nextadr; i++) {
                                checkInit(exit.tree.pos(), vardecls[i].sym);
                            }
                        }
                    }
    

    pendingExits
    返回之前的状态

                } finally {
                    assignToInits(tree, initsPrev);
                    uninits.assign(uninitsPrev);
                    nextadr = nextadrPrev;
                    firstadr = firstadrPrev;
                    returnadr = returnadrPrev;
                }
    

    AssignAnalyzer 处理block

    block的处理尤为简单,按道理说着这里需要处理pendingExits

            public void visitBlock(JCBlock tree) {
                int nextadrPrev = nextadr;
                scan(tree.stats);
                nextadr = nextadrPrev;
            }
    

    AssignAnalyzer 典例if

    处理条件时,将inits和uninits各个分裂成true和false的部分。
    处理完身体后,再把true和false的部分合并成inits和uninits部分。
    1.先处理条件。
    条件为真时:bits时reset状态。先把initsWhenFalse和initsWhenTrue合并到inits,uninitsWhenFalse和uninitsWhenTrue合并到uninits。true的直接复制,false的为全量。
    条件为假时:bits时reset状态。先把initsWhenFalse和initsWhenTrue合并到inits,uninitsWhenFalse和uninitsWhenTrue合并到uninits。true的为全量,false的为复制。
    条件不能判定条件为真是为假。当不能确定真假时。将Inits和uninits分别拷贝到initsWhenFalse,initsWhenTrue和uninitsWhenFalse,uninitsWhenTrue。
    最后清空uninits和inits
    2.处理if的body部分:先initsWhenTrue拷贝到inits,把uninitsWhenTrue拷贝到uninits。然后处理body。如果没有elsepart则将之前的initsBeforeElse合并(取交集)到inits,uninitsBeforeElse合并(取交集)到uninits
    3.对于elsepart处理类似if部分。只不过条件共用了。

            public void visitIf(JCIf tree) {
                scanCond(tree.cond);
                final Bits initsBeforeElse = new Bits(initsWhenFalse);
                final Bits uninitsBeforeElse = new Bits(uninitsWhenFalse);
                assignToInits(tree.cond, initsWhenTrue);
                uninits.assign(uninitsWhenTrue);
                scan(tree.thenpart);
                if (tree.elsepart != null) {
                    final Bits initsAfterThen = new Bits(inits);
                    final Bits uninitsAfterThen = new Bits(uninits);
                    assignToInits(tree.thenpart, initsBeforeElse);
                    uninits.assign(uninitsBeforeElse);
                    scan(tree.elsepart);
                    andSetInits(tree.elsepart, initsAfterThen);
                    uninits.andSet(uninitsAfterThen);
                } else {
                    andSetInits(tree.thenpart, initsBeforeElse);
                    uninits.andSet(uninitsBeforeElse);
                }
            }
    
            /** Analyze a condition. Make sure to set (un)initsWhenTrue(WhenFalse)
             *  rather than (un)inits on exit.
             */
            void scanCond(JCTree tree) {
                if (tree.type.isFalse()) {
                    if (inits.isReset()) merge(tree);
                    initsWhenTrue.assign(inits);
                    initsWhenTrue.inclRange(firstadr, nextadr);
                    uninitsWhenTrue.assign(uninits);
                    uninitsWhenTrue.inclRange(firstadr, nextadr);
                    initsWhenFalse.assign(inits);
                    uninitsWhenFalse.assign(uninits);
                } else if (tree.type.isTrue()) {
                    if (inits.isReset()) merge(tree);
                    initsWhenFalse.assign(inits);
                    initsWhenFalse.inclRange(firstadr, nextadr);
                    uninitsWhenFalse.assign(uninits);
                    uninitsWhenFalse.inclRange(firstadr, nextadr);
                    initsWhenTrue.assign(inits);
                    uninitsWhenTrue.assign(uninits);
                } else {
                    scan(tree);
                    if (!inits.isReset())
                        split(tree.type != syms.unknownType);
                }
                if (tree.type != syms.unknownType) {
                    resetBits(inits, uninits);
                }
            }
    

    AssignAnalyzer 典例whileLoop

    1.先保留进入while之前的状态。
    2.先处理condition跟if的condition处理一致
    3.把initsWhenFalse复制到initsSkip,把uninitsWhenFalse复制到uninitsSkip待处理完while后合并
    4.把initsWhenTrue复制到inits,把uninitsWhenTrue复制到uninits待处理完成后合并。
    5.处理while的body。
    6.处理continue,continue跳转到自身
    7.处理合并:initsSkip复制到inits,uninitsSkip复制到uninits
    8.处理break,break跳出当前body。

            public void visitWhileLoop(JCWhileLoop tree) {
                ListBuffer<P> prevPendingExits = pendingExits;
                FlowKind prevFlowKind = flowKind;
                flowKind = FlowKind.NORMAL;
                final Bits initsSkip = new Bits(true);
                final Bits uninitsSkip = new Bits(true);
                pendingExits = new ListBuffer<>();
                int prevErrors = getLogNumberOfErrors();
                final Bits uninitsEntry = new Bits(uninits);
                uninitsEntry.excludeFrom(nextadr);
                do {
                    scanCond(tree.cond);
                    if (!flowKind.isFinal()) {
                        initsSkip.assign(initsWhenFalse) ;
                        uninitsSkip.assign(uninitsWhenFalse);
                    }
                    assignToInits(tree, initsWhenTrue);
                    uninits.assign(uninitsWhenTrue);
                    scan(tree.body);
                    resolveContinues(tree);
                    if (getLogNumberOfErrors() != prevErrors ||
                        flowKind.isFinal() ||
                        new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1) {
                        break;
                    }
                    uninits.assign(uninitsEntry.andSet(uninits));
                    flowKind = FlowKind.SPECULATIVE_LOOP;
                } while (true);
                flowKind = prevFlowKind;
                //a variable is DA/DU after the while statement, if it's DA/DU assuming the
                //branch is not taken AND if it's DA/DU before any break statement
                assignToInits(tree.body, initsSkip);
                uninits.assign(uninitsSkip);
                resolveBreaks(tree, prevPendingExits);
            }
    

    DataFlow读后感

    1. 源码的介绍程度:上面的源码我主要主要选取比较典型的活性分析中和赋初值分析中比较典型的语法树作为例子作为分析。其他的语法树暂时未涉及。这些已经得到我想要得到了。关于二进制操作的部分尚未做详细说明。
    2. 编译原理:数据流分析中的赋初值分析基本上和编译原理中的分析基本一致,遵循基本快的思想。
    3. 算法导论:此处暂时尚未涉及到常见的算法,或者编译本身就是一件浩大的算法。
    4. 对于此过程实现,对于我本次的目标印证情况,非常友好,尤其是变量赋初值的分析基本上和编译原理一致。。
    展开全文
  • javac源码详解openJDKSE8版本0前言背景目标读javac源码需要哪些知识如何下载javac源码如何调试运行javac源码 前言 我在学习了算法导论英文版和编译原理英文版之后。想印证中书中所说知识,是否被使用到,也相当于是...
  • Javac源码解读-书目录

    2019-09-27 07:13:32
    (2)Javac源码(说明其中哪个功能由哪个主要的类来完成) (3)Javac支持的命令及实现 2、文件加载与输出 (1)概述 编译一个Java源代码到class时会涉及到依赖文件的查找加载,并且需要将最终生成的class...
  • javac源码详解openJDKSE8版本1编译的主要过程lexerparseenterTreesprocessAnnotationsattributeflowdesugargenerate 编译的主要过程 lexer 读java文件,生成token parse 读token组装成tree enterTrees 读tree,...
  • 前言 最近又到了面试季,大家的技术都在提升,如果自己还是原地...但是在网上搜索了一个圈都没发现有针对APT原理分析的文章,所以本篇文件我们就根据javac源码彻底搞清楚APT的执行与设计。 阅读前提 1、了解APT是什
  • Javac1.7编译器源码分析_简介与安装

    千次阅读 2008-03-27 23:45:00
    导读: 1.1:简介: 1.1.1 javac版本 SUN公司在2006年11月份已将java语言编译器(javac)源码通过GPLv2的方式开源, 还建立了“the Open-Source JDK Community”,网址是:http://openjdk.java.net/   截止到写这篇文章...
  • java 源码分析 Javac-Research Java语言编译器(javac)源代码学习研究(包括代码注释、文档、用于代码分析的测试用例)
  • 该语法抽取自[url=http://download.java.net/openjdk/jdk6/]OpenJDK 6[/url] build 17中javac的语法分析器,[url=...
  • (openJDK)Javac1.7编译器源码分析_001_简介与安装 (第一次写Blog,此文只是本人的一点点学习记录,不经本人事先同意请勿随意转载,谢谢) 1.1:简介: 1.1.1 javac版本 SUN公司在2006年11月份已将java语言编译器...
  • javac

    2016-12-01 17:22:20
    javac把.java文件编译成jvm可以运行的.class文件,jvm再把.class文件编译成机器可以...1.javac有四个模块用于分析源码到字节码,词法分析器(Token流)-语法分析器(语法树)-语义分析器(注解)-代码生成器(字节码)
  • 本文先记录词法分析器的工作过程,文中出现的源码出自Jdk9 首先来看一下词法分析器设计的类图: Lexer和Parser:Javac的主要词法分析器的接口类 Scanner:Lexer的默认实现类,逐个读取java源文件的单个字符,...
  • 长期以来,这一直是NetBeans的一部分,它提供了专门针对Java编辑器的经过高度调整的Java编译器,即针对语法着色,代码完成等功能进行解析和词法分析。 先决条件 吉特 蚂蚁1.9.9或以上 JDK 8或更高版本(用于构建nb-...
  • Tomcat 源码分析-启动分析(1)

    千次阅读 2018-12-21 10:34:09
    Tomcat 源码分析-启动分析(1) 文章目录Tomcat 源码分析-启动分析(1)启动脚本从startup.bat开始执行catalina.bat文件参考资料 启动脚本 windows是.bat,linux就是.sh了,差不多一个意思。 启动脚本做的事情...
  • 9.2、准备 9.3、测试代码情景一 9.4、测试代码情景二 9.5、测试代码情景三 9.6、测试代码情景四 9.7、测试代码情景五 10、JVM类加载机制 11、双亲委派模型 12、ClassLoader源码分析 12、1.loadClass 12、2.find...
  • 语法分析器是将词法分析分析的Token流组建成更加结构化的语法树,也就是将一个个单词组装成一句话,一个完整的语句。哪些词组合在一起是主语、哪些是谓语、宾语、定语…要做进一步区分。 语法树及各种语法节点...
  • NULL 博文链接:https://eleopard.iteye.com/blog/1884323
  • Javac编译器

    2016-11-20 22:59:00
    One Compiler ... Hacking the OpenJDK compiler(JAVAC源码分析) http://www.ahristov.com/tutorial/java-compiler.html http://download.ja...
  • Javac是什么 通常,一个java文件会通过编译器编译成字节码文件.class,再又java虚拟机JVM翻译成计算机可执行的文件。 我们所知道的java语言有它自己的语法规范,同样的JVM也有它的语法规范,如何让java的语法规则去...
  • Javac编译器源代码分析

    千次阅读 2016-03-10 16:05:00
    Javac编译器的编译流程主要如下图:       各个阶段的作用:   词法分析:   主要的作用就是读入Java源代码文件,生成Token流,用到的类如下图:             Scanner调用nextToken()...
  • openJDK源码分析

    千次阅读 2013-11-01 21:12:22
    OpenJDK源码分析(1) 对于Java程序来说,JVM是一台完整的机器,可是对于真正的机器或OS来说,JVM只是其中一个进程而已。作为一个进程,它又是如何启动的呢?如下来一步一步分析。(注,本文分析的JDK源...
  • Hugo源码分析

    千次阅读 2016-11-05 22:41:54
    Hugo是JakeWharton大神开发的一个通过注解触发的Debug日志库。它是一个非常好的AOP框架,在Debug模式下,Hugo利用aspectj库来进行切面编程,插入日志...通过分析Hugo的代码能够对gradle以及aspectj有一个非常好的了解。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 13,760
精华内容 5,504
关键字:

javac源码分析

java 订阅