2、表达式的有效性检查
Java编译器在进行表达式有效性检查时,会同时给出对这个表达式的符号及类型的期望,同时也会给出表达式运行后的实际类型,在分析过程还会保存上下文环境,如下给出了这几个变量的定义:
/** Visitor argument: the current environment.
*/
Env<AttrContext> env;
/** Visitor argument: the currently expected proto-kind.
*
* 值取自Kinds类
*/
int protoKindsSymbol;
/** Visitor argument: the currently expected proto-type.
*
* 值取自TypeTags
*/
Type protoTypeTagsType;
/** Visitor argument: the error key to be generated when a type error occurs
*/
String errKey;
/** Visitor theComputedType: the computed type.
*/
Type theComputedType;
其中有个非常重要的方法,源代码如下:
/** Visitor method: attribute a tree, catching any completion failure
* exceptions. Return the tree's type.
*
* @param tree The tree to be visited.
* @param env The environment visitor argument.
* @param protoKindsSymbol The protoKindsSymbol visitor argument. 取Kinds类中的变量
* @param protoTypeTagsType The protoTypeTagsType visitor argument.
*/
public Type attribTree(JCTree tree, Env<AttrContext> env,
int protoKindsSymbol,
Type protoTypeTagsType,
String errKey) {
Env<AttrContext> prevEnv = this.env;
int prevPkind = this.protoKindsSymbol;
Type prevPt = this.protoTypeTagsType;
String prevErrKey = this.errKey;
try {
this.env = env;
this.protoKindsSymbol = protoKindsSymbol;
this.protoTypeTagsType = protoTypeTagsType;
this.errKey = errKey;
tree.accept(this);
if (tree == breakTree) {
throw new BreakAttr(env);
}
// 在这里返回的这个theComputedType,非常重要,代表了tree推断
// 出来的类型
return theComputedType;
} catch (SymbolCompletionFailure ex) {
tree.type = syms.errType;
return chk.completionError(tree.pos(), ex);
} finally {
this.env = prevEnv;
this.protoKindsSymbol = prevPkind;
this.protoTypeTagsType = prevPt;
this.errKey = prevErrKey;
}
}
任何一个基本表达式都会从左边开始分析,之前讲过基本表达式的一些形式,只有确定了第一个树节点的符号及类型才能对一个表达式从左到右来验证有效性。例如如下实例:
package org.rpgpoet;
import java.util.Random;
public interface Music {
Random[] wizards = new Random[4];
}
接口中声明了一个数组类型的变量wizards
package bazola;
public class Gabriel {
static int n = org.rpgpoet.Music.wizards.length;
}
求接口中声明的数组变量wizards的长度,并赋值给一个新声明的int类型的变量n。在这个JCVariableDecl语句中,赋值表达式的右侧org.rpgpoet.Music.wizards.lenth就是一个基本表达式。
在Attr的visitVarDef()方法中有如下方法调用:
attribExpr(tree.init, initEnv, vs.type);
其中的tree.init就是赋值表达式中右侧的表达式,initEnv表达上下文环境,vs.type是Type类型为int,调用的attribExpr()方法源码如下:
/** Derived visitor method: attribute an expression tree.
*/
public Type attribExpr(JCTree tree, Env<AttrContext> env, Type protoTypeTagsType) {
if(protoTypeTagsType.tag != ERROR19){
return attribTree(tree,env,VAL12, protoTypeTagsType,"incompatible.types" );
}else{
return attribTree(tree,env,VAL12,Type.noType,"incompatible.types");
}
}
可以看到调用了attribTree()方法,并且传递了两个参数VAL12与protoTypeTagsType,其中VAL12的值在方法attribTree()中赋值给了protoKindsSymbl,表示表达式的右值,而protoTypeTagsType为int类型,表示对这个表达式类型的期望是整型。
attribTree()方法中protoKindsSymbol取值来自于Kinds类中定义的常量,而protoTypeTagsType值为Type类型,其tags值来自TypeTags中定义的常量。这两个类在之前符号表介绍时曾详细介绍过。这里重点介绍一下Kinds类中的VAL常量,这个常量表示表达式的右值。例如下面的两个赋值表达式中
a = 1
a = a +1
表达式的右部描述了一个值,这个值可以是一个常量或者变量,而左部描述的是值的存储位置,所以像1这样的常量不能出现在左部。对表达式org.rpgpoet.Music.wizards.lenth的符号期望也是一个VAL12,表示可以是一个变量或者常量。
现在要对这个基本表达式的整体进行处理,分析从左开始,这样就需要递归调用attribTree()方法来分析,直到找到最左的树节点com。
3、类型的隐式转换
ddddddddd
dddddddddddddd