/* * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ package org.visage.tools.comp; import org.visage.tools.tree.*; import com.sun.tools.mjavac.code.Symbol; import com.sun.tools.mjavac.code.Symbol.VarSymbol; import com.sun.tools.mjavac.util.Context; import static org.visage.tools.code.VisageFlags.*; import org.visage.api.VisageBindStatus; import org.visage.tools.code.VisageTypes; import org.visage.tools.code.VisageVarSymbol; import org.visage.tools.comp.VisageCheck.ForwardReferenceChecker; import com.sun.tools.mjavac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.mjavac.util.Name; import java.util.EnumSet; /** * * @author Robert Field */ public class VisageVarUsageAnalysis extends VisageTreeScanner { protected static final Context.Key<VisageVarUsageAnalysis> varUsageKey = new Context.Key<VisageVarUsageAnalysis>(); private boolean inLHS; private VisageBindStatus bindStatus; private VisageClassDeclaration currentClass; private VisageTypes types; private Name.Table names; private VisageDefs defs; public static VisageVarUsageAnalysis instance(Context context) { VisageVarUsageAnalysis instance = context.get(varUsageKey); if (instance == null) instance = new VisageVarUsageAnalysis(context); return instance; } VisageVarUsageAnalysis(Context context) { context.put(varUsageKey, this); names = Name.Table.instance(context); defs = VisageDefs.instance(context); types = VisageTypes.instance(context); inLHS = false; bindStatus = VisageBindStatus.UNBOUND; } public void analyzeVarUse(VisageEnv<VisageAttrContext> attrEnv) { //TODO: if cleared, must be at script-level: new ClearOldMarks().scan(attrEnv.tree); scan(attrEnv.tree); } // Clear flags for vars defined in this script private class ClearOldMarks extends VisageTreeScanner { private long ALL_MARKED_VARUSE = VARUSE_BOUND_INIT | VARUSE_TMP_IN_INIT_EXPR | VARUSE_FORWARD_REFERENCE | VARUSE_SELF_REFERENCE | VARUSE_OBJ_LIT_INIT; private void clearMark(Symbol sym) { sym.flags_field &= ~ALL_MARKED_VARUSE; } @Override public void visitVar(VisageVar tree) { super.visitVar(tree); clearMark(tree.sym); } @Override public void visitOverrideClassVar(VisageOverrideClassVar tree) { super.visitOverrideClassVar(tree); clearMark(tree.sym); } } private void mark(Symbol sym, long flag) { sym.flags_field |= flag; } private void markVarAccess(Symbol sym) { if (sym instanceof VarSymbol && (sym.flags_field & VARUSE_SPECIAL) == 0L) { if (bindStatus.isBound()) { mark(sym, VARUSE_BIND_ACCESS); // Accessors are necessary to send notification. mark(sym, VARUSE_NEED_ACCESSOR); if (bindStatus.isBidiBind()) mark(sym, VARUSE_ASSIGNED_TO); } else { if (inLHS) { // note the assignment the assignment mark(sym, VARUSE_ASSIGNED_TO); } } sym.flags_field &= ~VARUSE_OPT_TRIGGER; checkExternallySeen(sym); } if (sym instanceof VisageVarSymbol) { ((VisageVarSymbol)sym).setUsedOutsideSizeof(); } } private void checkExternallySeen(Symbol sym) { if (sym instanceof VisageVarSymbol && sym.owner != currentClass.sym) { ((VisageVarSymbol)sym).setIsExternallySeen(); } } @Override public void visitScript(VisageScript tree) { inLHS = false; bindStatus = VisageBindStatus.UNBOUND; super.visitScript(tree); } @Override public void visitVarInit(VisageVarInit tree) { Symbol sym = tree.getVar().sym; if (sym instanceof VisageVarSymbol) { ((VisageVarSymbol)sym).setHasVarInit(); } } private void scanVar(VisageAbstractVar tree) { VisageBindStatus wasBindStatus = bindStatus; bindStatus = tree.getBindStatus(); if (bindStatus.isBound()) { mark(tree.sym, VARUSE_BOUND_INIT); mark(tree.sym, VARUSE_NEED_ACCESSOR); } if (tree.getInitializer() != null) { tree.sym.flags_field |= VARUSE_TMP_IN_INIT_EXPR; scan(tree.getInitializer()); tree.sym.flags_field &= ~VARUSE_TMP_IN_INIT_EXPR; if (!tree.isLiteralInit()) { mark(tree.sym, VARUSE_NON_LITERAL); } } bindStatus = wasBindStatus; if (tree.getOnReplace() != null) { mark(tree.sym, VARUSE_HAS_REPLACE_TRIGGER); mark(tree.sym, VARUSE_NEED_ACCESSOR); scan(tree.getOnReplace()); } if (tree.getOnInvalidate() != null) { mark(tree.sym, VARUSE_NEED_ACCESSOR); scan(tree.getOnInvalidate()); } checkExternallySeen(tree.sym); } @Override public void visitVar(VisageVar tree) { scanVar(tree); } @Override public void visitVarRef(VisageVarRef tree) { mark(tree.getVarSymbol(), VARUSE_NEED_ACCESSOR | VARUSE_VARREF); } @Override public void visitOverrideClassVar(VisageOverrideClassVar tree) { scanVar(tree); mark(tree.sym, OVERRIDE); } @Override public void visitOnReplace(VisageOnReplace tree) { if (tree.getOldValue() != null) mark(tree.getOldValue().sym, VARUSE_OPT_TRIGGER); if (tree.getNewElements() != null) mark(tree.getNewElements().sym, VARUSE_OPT_TRIGGER); super.visitOnReplace(tree); } @Override public void visitClassDeclaration(VisageClassDeclaration tree) { VisageClassDeclaration previousClass = currentClass; currentClass = tree; // these start over in a class definition boolean wasLHS = inLHS; VisageBindStatus wasBindStatus = bindStatus; inLHS = false; bindStatus = VisageBindStatus.UNBOUND; super.visitClassDeclaration(tree); if (tree.isScriptClass) { markForwardReferences(tree); } bindStatus = wasBindStatus; inLHS = wasLHS; currentClass = previousClass; } @Override public void visitFunctionDefinition(VisageFunctionDefinition tree) { // these start over in a function definition boolean wasLHS = inLHS; VisageBindStatus wasBindStatus = bindStatus; inLHS = false; bindStatus = tree.getBindStatus(); // don't use super, since we don't want to cancel the inBindContext for (VisageVar param : tree.getParams()) { scan(param); } scan(tree.getBodyExpression()); bindStatus = wasBindStatus; inLHS = wasLHS; } @Override public void visitFunctionValue(VisageFunctionValue tree) { // these start over in a function value boolean wasLHS = inLHS; VisageBindStatus wasBindStatus = bindStatus; inLHS = false; bindStatus = VisageBindStatus.UNBOUND; super.visitFunctionValue(tree); bindStatus = wasBindStatus; inLHS = wasLHS; } @Override public void visitFunctionInvocation(VisageFunctionInvocation tree) { super.visitFunctionInvocation(tree); } @Override public void visitObjectLiteralPart(VisageObjectLiteralPart tree) { VisageBindStatus wasBindStatus = bindStatus; bindStatus = tree.getBindStatus(); mark(tree.sym, VARUSE_OBJ_LIT_INIT); scan(tree.getExpression()); checkExternallySeen(tree.sym); bindStatus = wasBindStatus; } @Override public void visitAssign(VisageAssign tree) { boolean wasLHS = inLHS; inLHS = true; scan(tree.lhs); inLHS = wasLHS; scan(tree.rhs); } @Override public void visitAssignop(VisageAssignOp tree) { boolean wasLHS = inLHS; inLHS = true; scan(tree.lhs); inLHS = wasLHS; scan(tree.rhs); } @Override public void visitUnary(VisageUnary tree) { boolean wasLHS = inLHS; VisageVarSymbol sym = null; boolean restoreOptTrigger = false; boolean restoreUsedOutsideSizeof = false; switch (tree.getVisageTag()) { case PREINC: case PREDEC: case POSTINC: case POSTDEC: inLHS = true; break; case SIZEOF: if (tree.arg instanceof VisageIdent) { sym = (VisageVarSymbol) ((VisageIdent) tree.arg).sym; restoreOptTrigger = (sym.flags_field & VARUSE_OPT_TRIGGER) != 0; restoreUsedOutsideSizeof = ! sym.isUsedOutsideSizeof(); sym.setUsedInSizeof(); } } scan(tree.arg); if (restoreOptTrigger) { sym.flags_field |= VARUSE_OPT_TRIGGER; } if (restoreUsedOutsideSizeof) sym.clearUsedOutsideSizeof(); inLHS = wasLHS; } @Override public void visitIdent(VisageIdent tree) { markVarAccess(tree.sym); } @Override public void visitInitDefinition(VisageInitDefinition that) { assert !inLHS : "cannot have init blocks on LHS"; assert !bindStatus.isBound() : "cannot have init blocks on bind"; scan((VisageBlock)that.getBody()); that.sym.owner.flags_field |= CLASS_HAS_INIT_BLOCK; } @Override public void visitInterpolateValue(final VisageInterpolateValue tree) { VisageBindStatus wasBindStatus = bindStatus; bindStatus = VisageBindStatus.UNIDIBIND; mark(tree.sym, VARUSE_OBJ_LIT_INIT); super.visitInterpolateValue(tree); bindStatus = wasBindStatus; } @Override public void visitSelect(VisageSelect tree) { // this may or may not be in a LHS but in either // event the selector is a value expression boolean wasLHS = inLHS; inLHS = false; scan(tree.selected); inLHS = wasLHS; markVarAccess(tree.sym); } @Override public void visitSequenceInsert(VisageSequenceInsert tree) { boolean wasLHS = inLHS; inLHS = true; scan(tree.getSequence()); inLHS = wasLHS; scan(tree.getElement()); } @Override public void visitSequenceDelete(VisageSequenceDelete tree) { boolean wasLHS = inLHS; inLHS = true; scan(tree.getSequence()); inLHS = wasLHS; scan(tree.getElement()); } @Override public void visitSequenceIndexed(VisageSequenceIndexed tree) { Symbol sym; boolean restoreOptTrigger; if (tree.getSequence() instanceof VisageIdent) { sym = ((VisageIdent) tree.getSequence()).sym; restoreOptTrigger = (sym.flags_field & VARUSE_OPT_TRIGGER) != 0; } else { sym = null; restoreOptTrigger = false; } scan(tree.getSequence()); if (restoreOptTrigger) sym.flags_field |= VARUSE_OPT_TRIGGER; boolean wasLHS = inLHS; inLHS = false; scan(tree.getIndex()); inLHS = wasLHS; } @Override public void visitSequenceSlice(VisageSequenceSlice tree) { scan(tree.getSequence()); boolean wasLHS = inLHS; inLHS = false; scan(tree.getFirstIndex()); scan(tree.getLastIndex()); inLHS = wasLHS; } @Override public void visitForExpressionInClause(VisageForExpressionInClause that) { scan(that.getVar()); if (bindStatus.isBound()) { mark(that.getVar().sym, VARUSE_ASSIGNED_TO); } Symbol sym = null; boolean restoreOptTrigger = false; VisageExpression seq = that.getSequenceExpression(); if (seq instanceof VisageIdent) { sym = ((VisageIdent) seq).sym; restoreOptTrigger = (sym.flags_field & VARUSE_OPT_TRIGGER) != 0; } else if (seq instanceof VisageSequenceSlice) { VisageSequenceSlice slice = (VisageSequenceSlice) seq; VisageExpression sseq = slice.getSequence(); if (sseq instanceof VisageIdent) { sym = ((VisageIdent) sseq).sym; restoreOptTrigger = (sym.flags_field & VARUSE_OPT_TRIGGER) != 0; } } scan(seq); if (restoreOptTrigger) sym.flags_field |= VARUSE_OPT_TRIGGER; scan(that.getWhereExpression()); } VisageOnReplace findOnReplace(Symbol sym, VisageOnReplace current) { //for return null; } void markForwardReferences(VisageTree tree) { new ForwardReferenceChecker(names, types, defs, EnumSet.allOf(ForwardReferenceChecker.ScopeKind.class)) { @Override protected void reportForwardReference(DiagnosticPosition pos, boolean selfReference, Symbol s, boolean potential) { if (selfReference) { mark(s, VARUSE_SELF_REFERENCE); } else { mark(s, VARUSE_FORWARD_REFERENCE); mark(s, VARUSE_NEED_ACCESSOR); } } }.scan(tree); } }