package com.sun.tools.javac.comp; import static com.sun.tools.javac.code.Flags.BLOCK; import static com.sun.tools.javac.code.Flags.FINAL; import static com.sun.tools.javac.code.Flags.STATIC; import static com.sun.tools.javac.code.Kinds.MTH; import static com.sun.tools.javac.code.Kinds.TYP; import com.sun.tools.javac.code.RPL; import com.sun.tools.javac.code.RPLElement; import com.sun.tools.javac.code.Symbol.MethodSymbol; import com.sun.tools.javac.code.Symbol.RegionParameterSymbol; import com.sun.tools.javac.code.Symbol.VarSymbol; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.TreeScanner; import com.sun.tools.javac.tree.JCTree.DPJForLoop; import com.sun.tools.javac.tree.JCTree.DPJRegionParameter; import com.sun.tools.javac.tree.JCTree.DPJRegionPathList; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCCase; import com.sun.tools.javac.tree.JCTree.JCCatch; import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop; import com.sun.tools.javac.tree.JCTree.JCForLoop; import com.sun.tools.javac.tree.JCTree.JCIdent; import com.sun.tools.javac.tree.JCTree.JCLabeledStatement; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; import com.sun.tools.javac.tree.JCTree.JCNewClass; import com.sun.tools.javac.tree.JCTree.DPJRegionDecl; import com.sun.tools.javac.tree.JCTree.JCSwitch; import com.sun.tools.javac.tree.JCTree.JCTry; import com.sun.tools.javac.tree.JCTree.JCTypeParameter; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Name; import com.sun.tools.javac.util.Pair; /** This is a specialization of TreeScanner that reconstructs the * attribution environment of each node as it scans the tree. It is * useful for passes such as effect checking and the porting tool * that need environment information but don't want to depend on * Attr. * * This class factors the environment-building logic out of Attr. In * theory, Attr could be built from this class too (leading to a tighter * design and less code duplication) but that would require some more * engineering. * * This pass assumes that any nodes you want to process have already been * attributed. Non-attributed symbols (i.e., null symbols in the tree) * should be ignored, though I haven't thoroughly tested that assertion. * If any null pointer errors crop up, just add some more code saying * "if this symbol is null, don't add it to the environment." * * @author Rob Bocchino * @author Mohsen Vakilian */ public abstract class EnvScanner extends TreeScanner { final Name.Table names; final MemberEnter memberEnter; protected Enter enter; protected EnvScanner(Context context) { names = Name.Table.instance(context); memberEnter = MemberEnter.instance(context); enter = Enter.instance(context); } public void setEnter(Enter enter) { this.enter = enter; } /* ************************************************************************ * Visitor methods *************************************************************************/ /** * Visitor argument: the environment that came in from our parent. */ protected Env<AttrContext> parentEnv; /** * Visitor return value: the environment(s) used to process our children. */ protected List<Env<AttrContext>> childEnvs; @Override public void visitClassDef(JCClassDecl tree) { Env<AttrContext> savedEnv = parentEnv; parentEnv = enter.typeEnvs.get(tree.sym); if (parentEnv != null) super.visitClassDef(tree); childEnvs = List.of(parentEnv); parentEnv = savedEnv; } @Override public void visitApply(JCMethodInvocation tree) { Env<AttrContext> savedEnv = parentEnv; parentEnv = parentEnv.dup(tree, parentEnv.info.dup()); super.visitApply(tree); childEnvs = List.of(parentEnv); parentEnv = savedEnv; } @Override public void visitBlock(JCBlock tree) { Env<AttrContext> savedEnv = parentEnv; if (parentEnv.info.scope.owner.kind == TYP) { // Block is a static or instance initializer; // let the owner of the environment be a freshly // created BLOCK-method. parentEnv = parentEnv.dup(tree, parentEnv.info.dup(parentEnv.info.scope.dupUnshared())); parentEnv.info.scope.owner = new MethodSymbol(tree.flags | BLOCK, names.empty, null, parentEnv.info.scope.owner); if ((tree.flags & STATIC) != 0) parentEnv.info.staticLevel++; super.visitBlock(tree); } else { // Create a new local environment with a local scope. parentEnv = parentEnv.dup(tree, parentEnv.info.dup(parentEnv.info.scope.dup())); super.visitBlock(tree); parentEnv.info.scope.leave(); } childEnvs = List.of(parentEnv); parentEnv = savedEnv; } @Override public void visitDPJForLoop(DPJForLoop tree) { Env<AttrContext> savedEnv = parentEnv; parentEnv = parentEnv.dup(parentEnv.tree, parentEnv.info.dup(parentEnv.info.scope.dup())); super.scan(tree.var); super.scan(tree.start); if (tree.length != null) super.scan(tree.length); if (tree.stride != null) super.scan(tree.stride); parentEnv.tree = tree; // before, we were not in loop! super.scan(tree.body); parentEnv.info.scope.leave(); childEnvs = List.of(parentEnv); parentEnv = savedEnv; } @Override public void visitForeachLoop(JCEnhancedForLoop tree) { Env<AttrContext> savedEnv = parentEnv; parentEnv = parentEnv.dup(parentEnv.tree, parentEnv.info.dup(parentEnv.info.scope.dup())); super.scan(tree.var); parentEnv.tree = tree; // before, we were not in loop! scan(tree.body); parentEnv.info.scope.leave(); childEnvs = List.of(parentEnv); parentEnv = savedEnv; } @Override public void visitForLoop(JCForLoop tree) { Env<AttrContext> savedEnv = parentEnv; parentEnv = parentEnv.dup(parentEnv.tree, parentEnv.info.dup(parentEnv.info.scope.dup())); super.scan(tree.init); if (tree.cond != null) super.scan(tree.cond); parentEnv.tree = tree; // before, we were not in loop! super.scan(tree.step); super.scan(tree.body); parentEnv.info.scope.leave(); childEnvs = List.of(parentEnv); parentEnv = savedEnv; } @Override public void visitLabelled(JCLabeledStatement tree) { Env<AttrContext> savedEnv = parentEnv; parentEnv = parentEnv.dup(tree); super.scan(tree.body); childEnvs = List.of(parentEnv); parentEnv = savedEnv; } @Override public void visitMethodDef(JCMethodDecl tree) { Env<AttrContext> savedEnv = parentEnv; MethodSymbol m = tree.sym; // Create a new environment with local scope // for attributing the method. parentEnv = memberEnter.methodEnv(tree, parentEnv); if (tree.paramInfo != null) { // Enter all region parameters into the local method scope. for (List<DPJRegionParameter> l = tree.paramInfo.rplParams; l.nonEmpty(); l = l.tail) { RegionParameterSymbol sym = l.head.sym; parentEnv.info.scope.enterIfAbsent(sym); } // Enter region constraints ListBuffer<Pair<RPL,RPL>> buf = ListBuffer.lb(); for (Pair<DPJRegionPathList,DPJRegionPathList> treeConstraint : tree.paramInfo.rplConstraints) { Pair<RPL,RPL> constraint = new Pair<RPL,RPL>(treeConstraint.fst.rpl, treeConstraint.snd.rpl); buf.append(constraint); } List<Pair<RPL,RPL>> constraints = buf.toList(); parentEnv.info.constraints.disjointRPLs = parentEnv.info.constraints.disjointRPLs.appendList(constraints); //parentEnv.info.constraintsOld = parentEnv.info.constraints.disjointRPLs; } // Enter all type parameters into the local method scope. for (List<JCTypeParameter> l = tree.typarams; l.nonEmpty(); l = l.tail) parentEnv.info.scope.enterIfAbsent(l.head.type.tsym); // Visit value parameters. for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) { super.scan(l.head); } if (tree.body != null) { // Visit method body. super.scan(tree.body); } parentEnv.info.scope.leave(); childEnvs = List.of(parentEnv); parentEnv = savedEnv; } @Override public void visitNewClass(JCNewClass tree) { Env<AttrContext> savedEnv = parentEnv; // The local environment of a class creation is // a new environment nested in the current one. parentEnv = parentEnv.dup(tree, parentEnv.info.dup()); super.visitNewClass(tree); childEnvs = List.of(parentEnv); parentEnv = savedEnv; } @Override public void visitSwitch(JCSwitch tree) { super.scan(tree.selector); Env<AttrContext> savedEnv = parentEnv; Env<AttrContext> switchEnv = parentEnv.dup(tree, parentEnv.info.dup(parentEnv.info.scope.dup())); ListBuffer<Env<AttrContext>> buffer = ListBuffer.lb(); for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) { parentEnv = switchEnv.dup(tree, switchEnv.info.dup(switchEnv.info.scope.dup())); super.scan(l.head); parentEnv.info.scope.leave(); buffer.append(parentEnv); } switchEnv.info.scope.leave(); childEnvs = buffer.toList(); parentEnv = savedEnv; } @Override public void visitTry(JCTry tree) { // Visit body super.scan(tree.body); Env<AttrContext> savedEnv = parentEnv; // Attribute catch clauses for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { JCCatch c = l.head; parentEnv = savedEnv.dup(c, savedEnv.info.dup(savedEnv.info.scope.dup())); super.scan(c.param); super.scan(c.body); parentEnv.info.scope.leave(); } // Attribute finalizer if (tree.finalizer != null) super.scan(tree.finalizer); childEnvs = List.of(parentEnv); parentEnv = savedEnv; } @Override public void visitVarDef(JCVariableDecl tree) { // Local variables have not been entered yet, so we need to do it now: if (parentEnv.info.scope.owner.kind == MTH) { if (tree.sym != null) parentEnv.info.scope.enter(tree.sym); } Env<AttrContext> savedEnv = parentEnv; VarSymbol v = tree.sym; if (v != null && tree.init != null) { if ((v.flags_field & FINAL) != 0 && tree.init.getTag() != JCTree.NEWCLASS) { // Nothing to do } else { // Attribute initializer in a new environment // with the declared variable as owner. // Check that initializer conforms to variable's declared type. parentEnv = memberEnter.initEnv(tree, parentEnv); super.scan(tree.init); } } super.visitVarDef(tree); childEnvs = List.of(parentEnv); parentEnv = savedEnv; } @Override public void visitRegionDecl(DPJRegionDecl tree) { if (parentEnv.info.scope.owner.kind == MTH) { if (tree.sym != null) { parentEnv.info.scope.enter(tree.sym); } } } }