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的容器,它提供了使用符号名访问符号的方法。
二、
虽然我们这一步的工作是填充符号表,但我发现并不是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.访问变量定义
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(); } }