aspect ExtractAssignment { // extract an expression as an assignment to a variable public VariableDeclaration Expr.extractAssignment(VariableDeclaration v, Stmt insertBefore) { if(inextractible()) throw new RefactoringException("expression is not extractible"); if(getParent() instanceof ExprStmt) { ExprStmt parent = (ExprStmt)getParent(); parent.setExpr(new AssignSimpleExpr(v.createLockedAccess(), this)); } else { Block block = enclosingBlock(); if(block == null) throw new RefactoringException("cannot extract assignment here"); // TODO: the following (if commented out?) is unsound; keep it for Eclipse compliance // if(!locationsWritten().isEmpty()) // throw new RefactoringException("cannot extract expression with side effects"); // lock and insert assignment before this stmt lockAllNames(); lockThrowEdges(); block.lockSyncDependencies(); VarAccess vacc = v.createLockedAccess(); replaceWith(vacc); VarAccess vaccDef = v.createLockedAccess(); insertBefore.insertStmtBefore(AssignExpr.asStmt(vaccDef, this)); block.hostType().flushCaches(); // check we could inline back if(!vacc.isAvailable(this)) throw new RefactoringException("extracted expression is not available"); SmallSet U = vaccDef.reachedUses(); assert(U.size() == 0 || U.size() == 1); if(U.size() != 1) throw new RefactoringException("extracted value is used more than once"); Access u = U.iterator().next(); SmallSet C = this.collectExceptionalSucc(); if(!C.isEmpty() && !vacc.isAnticipated((VariableDeclaration)vaccDef.decl())) throw new RefactoringException("expression may throw exception, but is not inlined along all paths"); for(CFGNode c : C) for(CFGNode w : u.precedingDefsUpTo(v)) if(!c.reachedUsesFor(((Access)w).getLocation()).isEmpty()) throw new RefactoringException("write to live variable may be lost"); if(u.isDest()) throw new RefactoringException("cannot extract as expr is a destination"); if(!u.reachingDefinitions().isSingleton()) throw new RefactoringException("ambiguous dataflow"); if(u.hostBodyDecl() != insertBefore.hostBodyDecl()) throw new RefactoringException("cannot inline across method boundaries"); hostType().flushCaches(); block.unlockSyncDependencies(); } return v; } public VariableDeclaration VariableDeclaration.extractAssignment(Expr expr) { return expr.extractAssignment(this, expr.enclosingStmt()); } }