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();
        }
    }

 

 
posted @ 2022-04-15 11:02  Mars.wang  阅读(842)  评论(0编辑  收藏  举报