aspect LockedVariableAccess { /* A locked variable access is a variable access that does not obey the normal variable lookup * rules, but instead immediately binds to its target. */ private Variable VarAccess.targetVariable = null; private void VarAccess.setTargetVariable(Variable var) { targetVariable = var; } public VarAccess Variable.createLockedAccess() { VarAccess va = new VarAccess(this.name()); va.lock(this); return va; } refine VariableScopePropagation eq VarAccess.decls() = targetVariable == null ? refined() : SimpleSet.emptySet.add(targetVariable); refine Uses protected void VarAccess.collectFieldUses(FieldDeclaration decl, String name, Collection col) { super.collectFieldUses(decl, name, col); if (targetVariable == decl || (name.equals(name()) && decl() == decl)) col.add(this); } refine GenericsExt protected void VarAccess.collectFieldUsesWithCopies(FieldDeclaration decl, String name, ArrayList col) { super.collectFieldUsesWithCopies(decl, name, col); if (targetVariable != null && targetVariable.sourceVariableDecl() == decl.sourceVariableDecl() || (name.equals(name()) && decl().sourceVariableDecl() == decl.sourceVariableDecl())) col.add(this); } refine PrettyPrint public void VarAccess.toString(StringBuffer s) { if(targetVariable == null) { refined(s); } else { s.append("[["); refined(s); s.append("]]"); } } // introducing locked variable accesses public ASTNode VarAccess.lockNames(Collection endangered) { if(endangered.contains(name())) return lock(); else return super.lockNames(endangered); } public ASTNode VarAccess.lock() { return targetVariable == null ? lock(decl()) : this; } public ASTNode VarAccess.lock(Variable target) { assert target != null; setTargetVariable(target); return this; } public boolean Variable.isSubstituted() { return false; } public boolean FieldDeclarationSubstituted.isSubstituted() { return true; } public boolean ParameterDeclarationSubstituted.isSubstituted() { return true; } public boolean VariableArityParameterDeclarationSubstituted.isSubstituted() { return true; } public boolean VarAccess.isLocked() { return targetVariable != null; } // eliminating locked variable accesses inh TypeDecl VarAccess.enclosingType(); public void VarAccess.eliminateLockedNames() { if(targetVariable != null) unlock(); super.eliminateLockedNames(); } public Access VarAccess.unlock() { Expr qual = getQualifier(); assert (qual==null || (parentDot() != null && qual == parentDot().getLeft())); Variable target = targetVariable.refresh(); setTargetVariable(null); flushCache(); if(fromSource()) setID(target.name()); if(decl().equals(target)) { return this; } else if(!fromSource()) { throw new RefactoringException("cannot fix variable access in compiled code"); } else { return unlock(qual, target); } } public Access VarAccess.unlock(Expr qual, Variable target) { if((qual == null ? inStaticContext() : qual.staticContextQualifier()) && target.isField() && !target.isStatic()) throw new RefactoringException("cannot access instance variable in static context"); SymbolicVarAccess acc = accessVariable(target); if(acc.isUnknownVarAccess()) { if((qual == null || qual.isPure()) && target.isStatic()) { TypeDecl host = target.hostType(); if(host.accessibleFrom(hostType()) && mayAccess((FieldDeclaration)target)) { this.flushCache(); Access hostAccess = host.createLockedAccess(); affectedByUnlock(hostAccess, this); return (Access) replace(qual!=null ? parentDot() : this).with(hostAccess.qualifiesAccess(this)); } } else if(qual != null && (qual.isThisAccess() || qual.isSuperAccess())) { acc = parentDot().accessVariable(target); if(acc != null && !acc.isUnknownVarAccess()) return acc.accessByModifyingAccess(this); } throw new RefactoringException("cannot access variable "+target.name()); } return acc.accessByModifyingAccess(this); } // eliminating locked variable accesses public Access SymbolicFieldAccess.accessByModifyingAccess(VarAccess originalAccess) { assert originalAccess != null; Expr qualifier = originalAccess.getQualifier(); TypeDecl enclosing = originalAccess.hostType(); VarAccess va = originalAccess; va.flushCache(); Expr to_replace = qualifier == null ? originalAccess : originalAccess.parentDot(); Access access = null; ASTNode parent = to_replace.replace(to_replace); if(target.isStatic()) { if (qualifier == null || qualifier.type() != target.hostType()) { Access hostAccess = target.hostType().createLockedAccess(); parent.affectedByUnlock(hostAccess, va); access = hostAccess.qualifiesAccess(va); } else { access = va; } } else { if(directlyVisible) { access = va; } else { if(qualifier == null) { if(source.equals(bend)) { if(bend.equals(enclosing)) { // this.f parent.affectedByUnlock(va); access = new ThisAccess("this").qualifiesAccess(va); } else { // B.this.f Access bendAccess = bend.createLockedAccess(); parent.affectedByUnlock(bendAccess, va); access = bendAccess.qualifiesAccess(new ThisAccess("this").qualifiesAccess(va)); } } else if(bend.isClassDecl() && source.equals(((ClassDecl)bend).superclass())) { if(bend.equals(enclosing)) { // super.f parent.affectedByUnlock(va); access = new SuperAccess("super").qualifiesAccess(va); } else { // B.super.f Access bendAccess = bend.createLockedAccess(); parent.affectedByUnlock(bendAccess, va); access = bendAccess.qualifiesAccess(new SuperAccess("super").qualifiesAccess(va)); } } else { va.lock(target); if(bend.equals(enclosing)) { // ((S)this).f Access sourceAccess = source.createLockedAccess(); parent.affectedByUnlock(sourceAccess, va); access = new ParExpr(new CastExpr(sourceAccess, new ThisAccess("this"))).qualifiesAccess(va); } else { // ((S)B.this).f Access sourceAccess = source.createLockedAccess(), bendAccess = bend.createLockedAccess(); parent.affectedByUnlock(sourceAccess, bendAccess, va); access = new ParExpr(new CastExpr(sourceAccess, bendAccess.qualifiesAccess( new ThisAccess("this")))).qualifiesAccess(va); } } } else { if(!bend.equals(qualifier.type())) throw new RefactoringException("cannot access variable"); if(source.equals(qualifier.type())) access = va; Access sourceAccess = source.createLockedAccess(); va.lock(target); parent.affectedByUnlock(sourceAccess, va); // "super" is treated as a normal expression in JastAddJ, but it actually isn't if(qualifier.isSuperAccess()) qualifier = qualifier.convertSuperToThis(); access = new ParExpr(new CastExpr(sourceAccess, qualifier)).qualifiesAccess(va); } } } access.flushCache(); if (qualifier != null) { qualifier.flushCaches(); } if (to_replace.equals(access)) return access; return (Access) parent.with(access); } public Access UnknownVarAccess.accessByModifyingAccess(VarAccess originalAccess) { throw new RefactoringException("cannot access variable"); } public Access LocalDeclaration.accessByModifyingAccess(VarAccess originalAccess) { if(originalAccess.getQualifier() != null) throw new RefactoringException("cannot qualify access to local variable"); return originalAccess; } }