Symbol类的一个实例代表一个符号。对于语法树来说,并不是每个节点都有一个符号实例。下面列举了哪些语法树节点具有符号的引用,如下表格:
其中JCNewClass、JCAssignOp、JCUnary、JCBinary、JCFieldAccess与JCIdent继承了JCExpression,可出现在表达式中。
如上的PackageSymbol、ClassSymbol、MethodSymbol、VarSymbol与TypeSymbol这些符号由用户指定,而剩下的一些都是对既有符号的引用。如语法节点JCAssignOp中的operator。
下面来举个具体的例子说明一下。
package m20170208;
public class A<T> {
public void test1() {
int a = 3;
int b = a + 2;
}
public void test2() {
A ins = new A();
ins.test1();
}
}
查看JCCompilationUnit语法节点的packge属性,如下:
查看JCClassDecl语法节点,如下:
查看JCTypeParameter节点。这个节点没有直接对Symbol进行引用,而是通过type属性的tsym来引用的,如下:
查看JCMethodDecl节点的sym属性,如下:
查看JCVariableDecl节点的sym属性,如下:
查看JCBinary节点的operator属性,如下:
查看JCIdent节点的sym属性,如下:
查看JCNewClass节点的constructor属性,如下:
查看JCFieldAccess节点的sym属性,如下:
下面来介绍一下Symbol中一些重要的属性。
属性1:kind
** The kind of this symbol.
* @see Kinds
*
* Java语言符号的种类,有包的符号、类型的符号、变量的符号、值的符号和方法的符号
*/
public int kind;
kind值取的是类Kinds中定义的一些常量,如下:
/** The empty set of kinds.
*/
public final static int NIL = 0; // 0
/** The kind of package symbols.
*/
public final static int PCK = 1 << 0; // 1
/** The kind of type symbols (classes, interfaces and type variables).
*/
public final static int TYP = 1 << 1; // 2
/** The kind of variable symbols.
*/
public final static int VAR = 1 << 2; // 4 变量符号
/** The kind of values (variables or non-variable expressions), includes VAR.
*
* 在标记阶段使用
*/
public final static int VAL = (1 << 3) | VAR; // 12 值(包括变量和非变量表达式),也包括变量
/** The kind of methods.
*/
public final static int MTH = 1 << 4; // 16 方法
/** The error kind, which includes all other kinds.包含所有其它类型的错误
*/
public final static int ERR = (1 << 5) - 1; // 31 包括剩下的其它种类
VAL一般是表达式的右端,例如:a = Exp;那么Exp可以是变量也可以是一个值,但是a绝对是一个变量。
OperatorSymbol的kind属性值为16,也就是方法。因为这些符号最终的意思是通过方法来体现的,如int a = b+c;假如b与c是int类型变量,那么调用了有两个int类型
参数的方法,将b与c传入后,返回的结果即是a想要的结果。
对比Symbol例子的截图查看kind的值。其中的NIL常量是在创建默认的noSymbol时使用过。在SymbolTable中的定义如下:
/** A symbol that stands for a missing symbol.
*/
public final TypeSymbol noSymbol;
属性2:flags_field
/** The flags of this symbol.
*
* 符号的标志
*/
public long flags_field; // 值来自Flags类中
Flags中有许多修饰符,如public、abstract等等,表示这个符号上有哪些修饰符。不过树节点中使用的修饰符与这里的flags_field未必是一个值,例如定义在接口中的变量,编译器默认会添加public、static、final修饰符,在记录符号时,这个flags_field也会将这些修饰符在long值中对应的位置为1。
属性3:attributes_field
/** The attributes of this symbol.
*
* 符号的属性
*/
public List<Attribute.Compound> attributes_field;
举个例子,如下:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.PACKAGE)
@Retention(RetentionPolicy.RUNTIME)
@interface MzTargetPackage {
public String version() default "";
}
定义一个包注解
@MzTargetPackage(version="1.0")
package m20170208;
在包上使用包注解,查看attributes_field属性的值,如下:
属性4:name
/** The name of this symbol in Utf8 representation.
* 符号的名称
*/
public Name name;
这个name就是NameImpl对象,在前面也讲解过。
属性5:type
/** The type of this symbol.
*
* 符号的类型
*/
public Type type;
有许多具体的Type类。
属性6:owner
/** The owner of this symbol.
*/
public Symbol owner;
从Symbol例子的各个截图中可以查看到各个owner的具体值。其中OperatorSymbol由于是预定义好的,所以其owner归属于预定义的ClassSymbol中。
举个例子,如下:
class Test{
Test o = new Test(){},x = o;
}
x这个VarSymbol还是属性ClassSymbol(Test),在词法分析阶段就是两个独立变量的声明,变为了如下形式:
class Test{
Test o = new Test(){};
Test x = o;
}
new Test(){}为JCNewClass,而del属性为JCClassDecl,如下截图。
其中的匿名类的owner为VarSymbol,如下:
属性6:erasure_field
/** A cache for the type erasure of this symbol.
*/
public Type erasure_field;
表示泛型擦除后的类型,如类A的泛型擦除后,其值如下:
又如JCNewClass中的MethodSymbol(A())中的此属性值,如下:
创建类对象的实例时,其实是调用的这个类的构造函数(被当作特殊的方法来对待)。、
方法1:public boolean isInheritedIn(Symbol clazz, Types types)
/** Is this symbol inherited into a given class?
* PRE: If symbol's owner is a interface,
* it is already assumed that the interface is a superinterface of given class.
* @param clazz The class for which we want to establish membership.
* This must be a subclass of the member's owner.
*/
public boolean isInheritedIn(Symbol clazz, Types types) {
switch ((int)(flags_field & Flags.AccessFlags)) { // AccessFlags中的值只能取其一
default: // error recovery
case PUBLIC:
return true;
case PRIVATE:
return this.owner == clazz;
case PROTECTED:
// we model interfaces as extending Object
// 非接口时为true,接口时为false,因为接口里没有public members
return (clazz.flags() & INTERFACE) == 0;
case 0: // default访问权限为同包或者同类,子类或者其它包是不可以访问的
PackageSymbol thisPackage = this.packge();
for (Symbol sup = clazz;
sup != null && sup != this.owner;
sup = types.supertype(sup.type).tsym
){
while (sup.type.tag == TYPEVAR14) {
sup = sup.type.getUpperBound().tsym;
}
if (sup.type.isErroneous()) {
return true; // error recovery
}
if ((sup.flags() & COMPOUND) != 0) {
/*
eg1:
interface IA{}
interface IB{}
class CA{}
public class TestMethod{
public <T extends CA&IA&IB>void methodA(){ }
}
*/
continue;
}
if (sup.packge() != thisPackage) { // 当修饰符为默认时,则不同的包就造成了错误
return false;
}
}
// 非接口时为true,接口时为false,因为接口里没有default members
return (clazz.flags() & INTERFACE) == 0;
}
}
如上的代码在检查某个symbol是否在继承于某个Symbl,例如:
class AA{
void test(){}
}
public class Outer<T extends AA> {
class Inner<D extends T>{
D d;
public void x(){
d.test();
}
}
}
要检查d.test()中的MethodSymbol时就需要判断是否由d这个TypeSymbol继承而来的MethodSymbol。test()方法定义在AA类中,并且修饰符为default,走case 0分支。