• java编译器源码解析语义分析填充符号表


    一、生成符号表

    填充符号表的核心逻辑在com.sun.tools.javac.comp.Enter类。

    在讨论填充符号表的逻辑之前,首先要明确一下,什么是符号?

    我们在java代码中,可能会声明一个类,类中有属性和方法,这些对于计算机而言,都是一种符号。

    在java编译器的实现中,定义了专门的符号类Symbol及相关的子类

     

    符号有名称,就是我们理解的类名、方法名和属性名。

    除此之外,符号还有类型,java专门设计了一套符号类型系统来标识它。

    我们举个例子

    int a=0;

    我们知道,a是一个变量,但编译器认为a是一个VarSymbol,它的类型是JCPrimitiveType.

    在执行程序的时候,原生类型和引用类型有不同的处理方式。

    原生类型可能存放在常量池中,但引用类型必须存在堆中,所以在编译器在编译期间就需要标识起来。

    填充符号表分为两个阶段:

    第一:类符号填充Enter

    主方法为complete()所有类都进入其作用域,在visit类声明的时候,给相关的JCClassDecl节点的sym类型定义了一个ClassSymbol的值。

    类符号填充完成后,进入第二阶段,调用enterMember.complete方法

    第二:其他成员符号填充

    逻辑在MemberEnter类的complete方法中

    MemberEnter.visitMethodDef()给方法声明节点的符号表填充符号。

    MemberEnter.visitVarDef()给变量声明节点的符号表填充符号。

    第三:其他的符号填充在后续阶段完成

    生成符号的同时,符号也被放入相应的Scope中。

    Scope又叫作用域,它是符号Symbol的容器,它提供了使用符号名访问符号的方法。

    Scope类被实现为具有“开放寻址”和“双重哈希”的哈希表。Scope是可以嵌套的。

    二、Symbol及其核心子类

    虽然我们这一步的工作是填充符号表,但我发现并不是JCTree所有的节点都有对应的符号,查看源码发现只有这些节点是有对应符号的。

    JCCompilationUnit里持有PackageSymbol属性,

    JCClassDecl持有ClassSymbol属性,属性名sym

    JCMethodDecl持有MethodSymbol属性

    JCVariableDecl持有VarSymbol属性

    JCNewClass持有Symbol类型的属性,属性名constructor

    JCAssignOp持有Symbol类型的属性,属性名operator

    JCUnary持有Symbol类型的属性,属性名operator

    JCBinary持有Symbol类型的属性,属性名operator

    JCFieldAccess持有Symbol属性

    JCIdent持有Symbol属性

    三、填充符号表

    3.1.访问类定义

    那么以我们的理解,当我们在AST上visit到一个类定义的时候,一定会生成一个ClassSymbol对象,然后放到符号表中。

    我们看下javac中的源码实现,验证一下我们的想法,下面是Enter.visitClassDef()

    我们可以看到在visitClassDef中的第三行声明了一个类符号——ClassSymbol c,经常一系列初始化后c被赋值给tree.sym,就在这个节点对应的符号属性

    同时我们还看到了作用域有关的信息,第二行就获取了当前作用域enclScope,经过一系列操作后,把当前符号c放入了作用域enclScope中,实际是将符号放入了Scope中的一些Entry数组中。

    public void visitClassDef(JCClassDecl tree) {
            Symbol owner = env.info.scope.owner;
            //获取当前作用域
            Scope enclScope = enterScope(env);
            //类符号的声明
            ClassSymbol c;
            if (owner.kind == PCK) {
                // 顶级类声明
                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.isEmpty() &&
                    !chk.checkUniqueClassName(tree.pos(), tree.name, enclScope)) {
                    result = null;
                    return;
                }
                if (owner.kind == TYP) {
                    // 内部类声明
                    c = reader.enterClass(tree.name, (TypeSymbol)owner);
                    if ((owner.flags_field & INTERFACE) != 0) {
                        tree.mods.flags |= PUBLIC | STATIC;
                    }
                } else {
                    // 局部类声明
                    c = reader.defineClass(tree.name, owner);
                    c.flatname = chk.localClassName(c);
                    if (!c.name.isEmpty())
                        chk.checkTransparentClass(tree.pos(), c, env.info.scope);
                }
            }
            //将符号放入当前AST节点的符号表中
            tree.sym = c;
    ​
            // Enter class into `compiled' table and enclosing scope.
            if (chk.compiled.get(c.flatname) != null) {
                duplicateClass(tree.pos(), c);
                result = types.createErrorType(tree.name, (TypeSymbol)owner, Type.noType);
                tree.sym = (ClassSymbol)result.tsym;
                return;
            }
            chk.compiled.put(c.flatname, c);
            //将类符号c放入当前作用域
            enclScope.enter(c);
    ​
            ...
        }

    3.2.访问方法定义

    访问定义在MemberEnter.visitMethodDef()中,与访问类定义类似,首先获取了当前作用域,创建方法符号,然后将符号赋值给当前节点的sym属性,并将符号加入当前作用域

    public void visitMethodDef(JCMethodDecl tree) {
            //获取当前作用域
            Scope enclScope = enter.enterScope(env);
            //创建方法符号
            MethodSymbol m = new MethodSymbol(0, tree.name, null, enclScope.owner);
            m.flags_field = chk.checkFlags(tree.pos(), tree.mods.flags, m, tree);
            //将方法符号赋值给当前节点
            tree.sym = m;
    ​
            //if this is a default method, add the DEFAULT flag to the enclosing interface
            if ((tree.mods.flags & DEFAULT) != 0) {
                m.enclClass().flags_field |= DEFAULT;
            }
            //创建当前方法的局部变量作用域
            Env<AttrContext> localEnv = methodEnv(tree, env);
    ​
            annotate.enterStart();
            try {
                ...
                //将所有参数加入局部变量作用域
                m.type = signature(m, tree.typarams, tree.params,
                                       tree.restype, tree.recvparam,
                                       tree.thrown,
                                       localEnv);
    ​
                //删除当前局部作用域的所有entry
                localEnv.info.scope.leave();
                if (chk.checkUnique(tree.pos(), m, enclScope)) {
                    //将方法符号加入当前作用域中
                enclScope.enter(m);
                }
               ...
            } finally {
                annotate.enterDone();
            }
        }

    3.3.访问变量定义

    MemberEnter.visitVarDef()

    public void visitVarDef(JCVariableDecl tree) {
            //获取当前变量所在环境
            Env<AttrContext> localEnv = env;
            if ((tree.mods.flags & STATIC) != 0 ||
                (env.info.scope.owner.flags() & INTERFACE) != 0) {
                localEnv = env.dup(tree, env.info.dup());
                localEnv.info.staticLevel++;
            }
            ...
            //获取当前作用域
                Scope enclScope = enter.enterScope(env);
            //创建变量符号
                VarSymbol v =
                    new VarSymbol(0, tree.name, tree.vartype.type, enclScope.owner);
                v.flags_field = chk.checkFlags(tree.pos(), tree.mods.flags, v, tree);
            //将符号赋值给当前节点的sym属性
                tree.sym = v;
               ...
                if (chk.checkUnique(tree.pos(), v, enclScope)) {
                    chk.checkTransparentVar(tree.pos(), v, enclScope);
                    //将变量符号加入作用域
                    enclScope.enter(v);
                }
                annotateLater(tree.mods.annotations, localEnv, v, tree.pos());
                typeAnnotate(tree.vartype, env, v, tree.pos());
                v.pos = tree.pos;
            } finally {
                annotate.enterDone();
            }
        }
     
  • 相关阅读:
    ATPCS规则
    ARM硬件问题
    ATPCS规则2
    12、深入理解计算机系统笔记:程序链接(1)
    11、深入理解计算机系统笔记:存储器层次结构,利用局部性
    15、深入理解计算机系统笔记:进程控制
    16、深入理解计算机系统笔记:信号
    9、深入理解计算机系统笔记:存储器层次结构,高速缓存存储器(1)
    *13、深入理解计算机系统笔记:程序链接(2)
    7、深入理解计算机系统笔记:存储器层次结构,存储技术(2)
  • 原文地址:https://www.cnblogs.com/wangbin2188/p/16148121.html
Copyright © 2020-2023  润新知