/* * This file is part of the X10 project (http://x10-lang.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.opensource.org/licenses/eclipse-1.0.php * * This file was originally derived from the Polyglot extensible compiler framework. * * (C) Copyright 2000-2007 Polyglot project group, Cornell University * (C) Copyright IBM Corporation 2007-2012. */ package polyglot.visit; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import polyglot.ast.Node; import polyglot.ast.NodeFactory; import polyglot.frontend.*; import polyglot.main.Reporter; import polyglot.types.*; import polyglot.types.Package; import polyglot.util.*; import x10.errors.Errors; import x10.types.X10ClassDef; import x10.types.X10ClassType; /** Visitor which traverses the AST constructing type objects. */ public class TypeBuilder extends NodeVisitor { protected ImportTable importTable; protected Job job; protected TypeSystem ts; protected Reporter reporter; protected NodeFactory nf; protected TypeBuilder outer; protected boolean inCode; // true if the last scope pushed as not a class. protected boolean global; // true if all scopes pushed have been classes. protected Package package_; protected X10ClassDef type; // last class pushed. protected Def def; public TypeBuilder(Job job, TypeSystem ts, NodeFactory nf) { this.job = job; this.ts = ts; this.reporter = ts.extensionInfo().getOptions().reporter; this.nf = nf; this.outer = null; } public TypeBuilder push() { TypeBuilder tb = (TypeBuilder) this.shallowCopy(); tb.outer = this; return tb; } public boolean inCode() { return inCode; } public TypeBuilder pop() { return outer; } public Def def() { return def; } public Job job() { return job; } public ErrorQueue errorQueue() { return job().compiler().errorQueue(); } public NodeFactory nodeFactory() { return nf; } public TypeSystem typeSystem() { return ts; } public NodeVisitor begin() { return this; } public Node override(Node n) { return n.del().buildTypesOverride(this); } public NodeVisitor enter(Node n) { return n.del().buildTypesEnter(this); } public Node leave(Node old, Node n, NodeVisitor v) { return n.del().buildTypes((TypeBuilder) v); } /** @deprecated */ public TypeBuilder pushContext(Context c) { LinkedList<Context> stack = new LinkedList<Context>(); while (c != null) { stack.addFirst(c); c = c.pop(); } TypeBuilder tb = this; boolean inCode = false; for (Iterator<Context> i = stack.iterator(); i.hasNext(); ) { c = (Context) i.next(); if (c.inCode()) { if (! inCode) { // entering code inCode = true; tb = tb.pushCode(c.currentCode()); } } else { if (c.importTable() != null && tb.importTable() == null) { // entering class file tb.setImportTable(c.importTable()); } if (c.importTable() != null && c.package_() != null && tb.currentPackage() == null) { // entering package context in source tb = tb.pushPackage(c.package_()); } if (c.currentClassDef() != tb.currentClass()) { // entering class tb = tb.pushClass(c.currentClassDef()); } } } return tb; } public TypeBuilder pushDef(Def def) { TypeBuilder tb = push(); tb.def = def; return tb; } public TypeBuilder pushPackage(Package p) { if (reporter.should_report(Reporter.visit, 4)) reporter.report(4, "TB pushing package " + p + ": " + context()); TypeBuilder tb = push(); tb.inCode = false; tb.package_ = p; return tb; } public TypeBuilder pushCode(CodeDef def) { if (reporter.should_report(Reporter.visit, 4)) reporter.report(4, "TB pushing code: " + context()); TypeBuilder tb = pushDef(def); tb.inCode = true; tb.global = false; return tb; } public TypeBuilder pushClass(X10ClassDef classDef) { if (reporter.should_report(Reporter.visit, 4)) reporter.report(4, "TB pushing class " + classDef + ": " + context()); TypeBuilder tb = pushDef(classDef); tb.inCode = false; tb.type = classDef; // Make sure the import table finds this class. if (importTable() != null && classDef.isTopLevel()) { tb.importTable().addExplicitImport(QName.make(classDef.fullName())); } return tb; } // Duplicate class id counter private static int dupId = 0; /** * Do not fail on duplicate types, but create another instance of the type with a * dummy name, to allow proceeding with compilation. */ protected ClassDef newClass(Position pos, Position errorPos, Flags flags, Name name) { return newClass(pos, errorPos, flags, name, null); } /** * Do not fail on duplicate types, but create another instance of the type with a * dummy name, to allow proceeding with compilation. */ protected X10ClassDef newClass(Position pos, Position errorPos, Flags flags, Name name, SemanticException error) { TypeSystem ts = typeSystem(); X10ClassDef ct = ts.createClassDef(job().source()); ct.position(pos); ct.errorPosition(errorPos); ct.flags(flags); ct.name(name); ct.superType(new ErrorRef_c<Type>(ts, pos, "Cannot get superclass before type-checking class declaration.")); if (inCode) { ct.kind(ClassDef.LOCAL); ct.outer(Types.ref(currentClass())); ct.setJob(job()); if (currentPackage() != null) { ct.setPackage(Types.<Package>ref(currentPackage())); } return ct; } else if (currentClass() != null) { ct.kind(ClassDef.MEMBER); ct.outer(Types.ref(currentClass())); ct.setJob(job()); currentClass().addMemberClass(Types.<ClassType>ref(ct.asType())); if (currentPackage() != null) { ct.setPackage(Types.<Package>ref(currentPackage())); } // if all the containing classes for this class are member // classes or top level classes, then add this class to the // parsed resolver. ClassDef container = currentClass(); boolean allMembers = (container.isMember() || container.isTopLevel()); while (container.isMember()) { container = container.outer().get(); allMembers = allMembers && (container.isMember() || container.isTopLevel()); } if (allMembers) { try { X10ClassType t = ct.asType(); if (error != null) t = t.error(error); typeSystem().systemResolver().addNamed(QName.make(currentClass().fullName(), ct.name()), t); // Save in the cache using the name a class file would use. QName classFileName = typeSystem().getTransformedClassName(ct); typeSystem().systemResolver().install(classFileName, t); } catch (SemanticException e) { Errors.issue(job, e); } } return ct; } else { ct.kind(ClassDef.TOP_LEVEL); ct.setJob(job()); QName fullName; if (currentPackage() != null) { ct.setPackage(Types.<Package>ref(currentPackage())); fullName = QName.make(currentPackage().fullName(), ct.name()); } else { fullName = QName.make(null, ct.name()); } List<Type> dups = typeSystem().systemResolver().check(fullName); Type dup = null; if (dups != null) { for (Type q : dups) { if (q instanceof ClassType && q.fullName().equals(fullName)) { dup = q; } } } if (dup != null && dup.fullName().equals(fullName)) { if (error == null) { error = new SemanticException("Duplicate class \"" + ct.fullName() + "\".", pos); Errors.issue(job, error); } Name newName = Name.make(name.toString()+"_dup"+(dupId++)); ct.name(newName); fullName = QName.make(null, newName); } try { X10ClassType t = ct.asType(); if (error != null) t = t.error(error); typeSystem().systemResolver().addNamed(fullName, t); } catch (SemanticException e) { Errors.issue(job, e); } return ct; } } public TypeBuilder pushAnonClass(Position pos) { if (reporter.should_report(Reporter.visit, 4)) reporter.report(4, "TB pushing anon class: " + this); if (! inCode) { throw new InternalCompilerError( "Can only push an anonymous class within code."); } X10ClassDef ct = createAnonClass(pos); return pushClass(ct); } protected X10ClassDef createAnonClass(Position pos) { TypeSystem ts = typeSystem(); X10ClassDef ct = ts.createClassDef(this.job().source()); ct.kind(ClassDef.ANONYMOUS); ct.outer(Types.ref(currentClass())); ct.position(pos); ct.setJob(job()); if (currentPackage() != null) { ct.setPackage(Types.<Package>ref(currentPackage())); } // ct.superType(Types.ref(ts.unknownType(pos))); return ct; } public TypeBuilder pushClass(Position pos, Position errorPos, Flags flags, Name name) { return pushClass(pos, errorPos, flags, name, null); } public TypeBuilder pushClass(Position pos, Position errorPos, Flags flags, Name name, SemanticException error) { X10ClassDef t = newClass(pos, errorPos, flags, name, error); return pushClass(t); } public X10ClassDef currentClass() { return this.type; } public Package currentPackage() { return package_; } public ImportTable importTable() { return importTable; } public void setImportTable(ImportTable it) { this.importTable = it; } public String context() { return "(TB " + type + (inCode ? " inCode" : "") + (global ? " global" : "") + (outer == null ? ")" : " " + outer.context() + ")"); } }