「類型名稱的消解」即類型的消解。類型名稱由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; }