“类型名称的消解”即类型的消解。类型名称由TypeRef 对象表示,类型由Type 对象表示。类型名称的消解就是将TypeRef 对象转换为Type 对象。java
TypeResolver 类的处理仅仅是遍历抽象语法树,发现TypeRef 的话就从叶子节点开始将其转换为Type 类型。类型和变量的不一样之处在于没有做用域的嵌套(做用域惟一),所以没
有必要使用栈。
【TypeRef 对象和Type 对象的对应关系保存在TypeTable 对象中。】node
其中Type为类型的定义。struct point { int x; int y; }; 是类型的定义。数组
TypeRef为类型的名称。struct point 是类型的名称,之因此特地将Type 类和TypeRef 类分开,是由于在类型定义以前就能够编写用到了该类型的代码。也就是说,能够编写以下所示的代码,C 语言中是不能够编写这样的代码的:函数
struct s var; struct s { int memb; };
类型名称的消解入口:this
/*入口 * */ // #@@range/resolveProgram{ public void resolve(AST ast) { /* * 首先调用defineTypes 方法,根据代码中定义的类型生成Type 对象,并保存到 TypeTable 对象中。经过import 导入的类型定义也在这里处理。 */ defineTypes(ast.types()); /*类型和抽象语法树的遍历. * 但defineTypes 方法不处理结构体成员的类型等TypeRef 对象。将抽象语法树中已有 的TypeRef 转换成Type 的处理将在下面的foreach 语句中执行。若是这两部分处理不分开进 行的话,在处理递归的类型定义时程序会陷入死循环。 ast.types()--源文件内外的类型定义 */ // #@@range/resolveProgram_core{ for (TypeDefinition t : ast.types()) { t.accept(this); } /* * 第2 个foreach 语句将使用import 从文件外部读入的定义、全局变量以及函数等全部剩余 的TypeRef 转换为Type。 ast.entities()--用import 导入的变量和函数的声明,以及源文件内的变量和函数的定义 */ for (Entity e : ast.entities()) { e.accept(this); } /* * 上面两个for循环遍历在源文件内外定义的全部类型、变量、函数,将其中所包含的TypeRef 对象 所有转换为Type 对象。 */ // #@@} }
首先对ast.types(),即StructNode(结构体定义)、UnionNode(联合体定义)、TypedefNode(用户类型定义)执行defineTypes:指针
/*类型的声明. * defineTypes 是将类型定义添加到TypeTable 对象的方法 */ // #@@range/defineTypes{ private void defineTypes(List<TypeDefinition> deftypes) { /* * 使用foreach 语句将deftypes 中的TypeDefinition 对象逐个取出, 将def. typeRef() 和def.definingType() 关联成对, 用typeTable.put 方法添加到 typeTable 中。def.typeRef() 返回的是该TypeDefinition 对象要定义的类型的 TypeRef(类型名称)。def.definingType() 返回的是该TypeDefinition 对象要定义的 Type(类型)。 */ for (TypeDefinition def : deftypes) { /* * 但若是typeTable.isDefined() 为true 的话,说明这个TypeRef 已经存在,这种情 况下取消添加处理并输出错误消息。 */ if (typeTable.isDefined(def.typeRef())) { error(def, "duplicated type definition: " + def.typeRef()); } else { /* * TypeDefinition 类是抽象类, 实际生成的实例是TypeDefinition 的子类 StructNode、UnionNode、TypedefNode。StructNode 表示结构体的定义,UnionNode 表示联合体的定义,TypedefNode 表示typedef 语句。 StructNode#definingType: public Type definingType() { return new StructType(name(), members(), location()); } 调用TypeTable#put 方法将生成的StrcutType 对 象添加到TypeTable 对象中。TypeTable 对象的内部保存有HashMap 对象, 所以 TypeTable#put 方法只需简单地调用HashMap#put 便可。 */ typeTable.put(def.typeRef(), def.definingType()); } } }
把上面三种类型的名称和类型都保存在typeTable中,注意typeTable初始化的时候已经自动把全部基本类型都put进去了。而后第一个for循环的三个visit方法:对象
// #@@range/StructNode{ public Void visit(StructNode struct) { resolveCompositeType(struct); return null; } // #@@} // #@@range/UnionNode{ public Void visit(UnionNode union) { resolveCompositeType(union); return null; } // #@@} // #@@range/TypedefNode{ public Void visit(TypedefNode typedef) { bindType(typedef.typeNode()); bindType(typedef.realTypeNode()); return null; } // #@@}
接着:blog
public void resolveCompositeType(CompositeTypeDefinition def) { CompositeType ct = (CompositeType)typeTable.get(def.typeNode().typeRef()); if (ct == null) { throw new Error("cannot intern struct/union: " + def.name()); } for (Slot s : ct.members()) { bindType(s.typeNode()); } } /* * 首先,用TypeNode#isResolved 方法检查是否已经完成了转换,若是已经完成,则即 刻使用return 结束处理。若是还未转换,用n.typeRef() 从TypeNode 中取出TypeRef, 再用typeTable.get 转换为Type 对象, 而后将此Type 对象用n.setType 设置到 TypeNode 中。 */ // #@@range/bindType{ private void bindType(TypeNode n) { if (n.isResolved()) return; n.setType(typeTable.get(n.typeRef())); }
也很简单,resolveCompositeType是针对每种类型的成员的类型检查,关键的类是TypeNode,从它里面获取TypeRef(类型的名称),再经过类型的名称从typeTable获取已有的类型的定义。而后获取到当前类型的全部的成员变量,再将这个成员变量的类型的名称和定义经过bindType方法绑定起来。typeTable其实是起到一个中转站的做用。递归
第二个for循环是将除了上面三种类型的全部剩余的TypeRef 转换为Type。好比:作用域
/* * 变量定义的类型消解. */ // #@@range/DefinedVariable{ public Void visit(DefinedVariable var) { /* * TypeRef 对象基本上都存放在TypeNode 对象中。TypeNode 是成对地保存TypeRef 和 Type 的对象,其目的在于简化TypeResolver 类的代码。 */ bindType(var.typeNode()); if (var.hasInitializer()) { visitExpr(var.initializer()); } return null; }
还有重要的函数类型:
/* * 函数定义的类型消解. */ // #@@range/DefinedFunction{ public Void visit(DefinedFunction func) { /* * 在函数定义中,以下这些地方存在TypeRef。 1. 返回值的类型 2. 形参的类型 3. 函数体的代码中 */ resolveFunctionHeader(func); visitStmt(func.body()); return null; } private void resolveFunctionHeader(Function func) { /* * resolveFunctionHeader 方法的第1 行用于处理返回值的类型。func.typeNode() 返回保存有返回值类型的TypeNode 对象,再调用bindType 方法将返回值的类型从 TypeRef 转换为Type。 */ bindType(func.typeNode()); /* * resolveFunctionHeader 方法从第2 行开始都是对形参进行的处理。用foreach 语句 * 对func.parameters() 进行遍历,取出表示形参的Parameter 对象。而后用param. typeNode() 取出Parameter 对象中的TypeNode 对象,将TypeRef 转换为Type。 */ for (Parameter param : func.parameters()) { // arrays must be converted to pointers in a function parameter. /* * 只有在将形参的TypeRef 转换为Type 时使用了TypeTable 类的getParamType 方法。 它和一般的get 方法的区别在于数组的TypeRef 会被转换为指针的Type。C 语言(C♭)中形 参类型是数组的状况下彻底等同于指针类型,所以在此处统一成为指针类型。 */ Type t = typeTable.getParamType(param.typeNode().typeRef()); param.typeNode().setType(t); } }
首先调用resolveFunctionHeader方法,里面第一行是绑定函数的返回类型,而后一个for循环绑定函数的全部形参类型。而后再调用visitStmt(func.body());绑定函数体的全部类型:
public Void visit(BlockNode node) { for (DefinedVariable var : node.variables()) { var.accept(this); } visitStmts(node.stmts()); return null; }