package org.jruby.ir.dataflow.analyses;
import org.jruby.ir.IRClosure;
import org.jruby.ir.IREvalScript;
import org.jruby.ir.IRScope;
import org.jruby.ir.dataflow.DataFlowProblem;
import org.jruby.ir.instructions.Instr;
import org.jruby.ir.instructions.ReceiveJRubyExceptionInstr;
import org.jruby.ir.instructions.StoreLocalVarInstr;
import org.jruby.ir.instructions.ThrowExceptionInstr;
import org.jruby.ir.operands.*;
import org.jruby.ir.representations.BasicBlock;
import org.jruby.ir.representations.CFG;
import java.util.HashSet;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
public class StoreLocalVarPlacementProblem extends DataFlowProblem<StoreLocalVarPlacementProblem, StoreLocalVarPlacementNode> {
public static final String NAME = "Placement of local-var stores";
private boolean scopeHasLocalVarStores;
private boolean scopeHasUnrescuedExceptions;
public StoreLocalVarPlacementProblem() {
super(DataFlowProblem.DF_Direction.FORWARD);
}
@Override
public String getName() {
return "Binding Stores Placement Analysis";
}
@Override
public StoreLocalVarPlacementNode buildFlowGraphNode(BasicBlock bb) {
return new StoreLocalVarPlacementNode(this, bb);
}
@Override
public String getDataFlowVarsForOutput() {
return "";
}
public boolean scopeHasLocalVarStores() {
return scopeHasLocalVarStores;
}
public boolean scopeHasUnrescuedExceptions() {
return scopeHasUnrescuedExceptions;
}
TemporaryLocalVariable getLocalVarReplacement(LocalVariable v, Map<Operand, Operand> varRenameMap) {
TemporaryLocalVariable value = (TemporaryLocalVariable)varRenameMap.get(v);
if (value == null) {
value = getScope().getNewTemporaryVariableFor(v);
varRenameMap.put(v, value);
}
return value;
}
boolean addScopeExitStoreLocalVars(ListIterator<Instr> instrs, Set<LocalVariable> dirtyVars, Map<Operand, Operand> varRenameMap) {
IRScope scope = getScope();
boolean addedStores = false;
boolean isEvalScript = scope instanceof IREvalScript;
for (LocalVariable v : dirtyVars) {
if (isEvalScript || !(v instanceof ClosureLocalVariable) || ((ClosureLocalVariable)v).isOuterScopeVar()) {
addedStores = true;
instrs.add(new StoreLocalVarInstr(scope, getLocalVarReplacement(v, varRenameMap), v));
}
}
return addedStores;
}
public void addStores(Map<Operand, Operand> varRenameMap) {
boolean mightRequireGlobalEnsureBlock = false;
Set<LocalVariable> dirtyVars = null;
IRScope cfgScope = getScope();
CFG cfg = cfgScope.getCFG();
this.scopeHasLocalVarStores = false;
this.scopeHasUnrescuedExceptions = false;
if (cfgScope instanceof IRClosure) {
mightRequireGlobalEnsureBlock = true;
dirtyVars = new HashSet<LocalVariable>();
}
for (StoreLocalVarPlacementNode bspn: flowGraphNodes) {
boolean bbAddedStores;
boolean bbHasUnrescuedExceptions = !bspn.hasExceptionsRescued();
if (mightRequireGlobalEnsureBlock && bbHasUnrescuedExceptions) {
bbAddedStores = bspn.addStores(varRenameMap, dirtyVars);
} else {
bbAddedStores = bspn.addStores(varRenameMap, null);
}
scopeHasUnrescuedExceptions = scopeHasUnrescuedExceptions || bbHasUnrescuedExceptions;
scopeHasLocalVarStores = scopeHasLocalVarStores || bbAddedStores;
}
if ((mightRequireGlobalEnsureBlock == true) && !dirtyVars.isEmpty()) {
ListIterator<Instr> instrs;
BasicBlock geb = cfg.getGlobalEnsureBB();
if (geb == null) {
Variable exc = cfgScope.createTemporaryVariable();
geb = new BasicBlock(cfg, Label.getGlobalEnsureBlockLabel());
geb.addInstr(new ReceiveJRubyExceptionInstr(exc));
geb.addInstr(new ThrowExceptionInstr(exc));
cfg.addGlobalEnsureBB(geb);
}
instrs = geb.getInstrs().listIterator(geb.getInstrs().size());
Instr i = instrs.previous();
assert i.getOperation().transfersControl(): "Last instruction of GEB in scope: " + getScope() + " is " + i + ", not a control-xfer instruction";
addScopeExitStoreLocalVars(instrs, dirtyVars, varRenameMap);
}
}
}