package org.jruby.ir.dataflow.analyses;
import org.jruby.dirgra.Edge;
import org.jruby.ir.dataflow.FlowGraphNode;
import org.jruby.ir.instructions.ClosureAcceptingInstr;
import org.jruby.ir.instructions.Instr;
import org.jruby.ir.instructions.ResultInstr;
import org.jruby.ir.interpreter.FullInterpreterContext;
import org.jruby.ir.operands.LocalVariable;
import org.jruby.ir.operands.Operand;
import org.jruby.ir.operands.TemporaryLocalVariable;
import org.jruby.ir.operands.Variable;
import org.jruby.ir.operands.WrappedIRClosure;
import org.jruby.ir.IRClosure;
import org.jruby.ir.representations.BasicBlock;
import java.util.*;
public class DefinedVariableNode extends FlowGraphNode<DefinedVariablesProblem, DefinedVariableNode> {
public DefinedVariableNode(DefinedVariablesProblem prob, BasicBlock n) {
super(prob, n);
}
@Override
public void init() {
out = null;
}
private void addDFVar(Variable v) {
if (!problem.dfVarExists(v)) problem.addDFVar(v);
}
@Override
public void buildDataFlowVars(Instr i) {
if (i instanceof ResultInstr) addDFVar(((ResultInstr) i).getResult());
for (Variable x: i.getUsedVariables()) {
addDFVar(x);
}
}
@Override
public void applyPreMeetHandler() {
setSize = problem.getDFVarsCount();
in = null;
}
@Override
public void compute_MEET(Edge e, DefinedVariableNode pred) {
BitSet predState = basicBlock.isRescueEntry() ? pred.in : pred.out;
if (predState != null) {
if (in == null) {
int n = predState.size();
in = new BitSet(n);
in.set(0, n);
}
in.and(predState);
}
}
@Override
public void initSolution() {
tmp = in == null ? new BitSet(setSize) : (BitSet) in.clone();
}
@Override
public void applyTransferFunction(Instr i) {
if (i instanceof ResultInstr) {
tmp.set(problem.getDFVar(((ResultInstr) i).getResult()));
}
for (Variable v: i.getUsedVariables()) {
if (v instanceof LocalVariable && ((LocalVariable)v).isOuterScopeVar()) {
tmp.set(problem.getDFVar(v));
}
}
}
private void identifyUndefinedVarsInClosure(Set<Variable> undefinedVars, IRClosure cl, int nestingLevel) {
FullInterpreterContext fic = cl.getFullInterpreterContext();
int clBaseDepth = nestingLevel + (fic.reuseParentDynScope() ? 0 : 1);
fic.setUpUseDefLocalVarMaps();
for (LocalVariable lv: fic.getUsedLocalVariables()) {
if (problem.getDFVar(lv) == null) {
continue;
}
if (lv.getScopeDepth() == clBaseDepth && !tmp.get(problem.getDFVar(lv))) {
undefinedVars.add(!lv.isOuterScopeVar() ? lv : lv.cloneForDepth(0));
tmp.set(problem.getDFVar(lv));
}
}
for (IRClosure nestedCl: cl.getClosures()) {
identifyUndefinedVarsInClosure(undefinedVars, nestedCl, nestingLevel + 1);
}
}
public void identifyInits(Set<Variable> undefinedVars) {
int parentScopeDepth = problem.getFIC().reuseParentDynScope() ? 0 : 1;
initSolution();
for (Instr i: basicBlock.getInstrs()) {
for (Variable v: i.getUsedVariables()) {
if (!v.isSelf()) {
if (v instanceof LocalVariable) {
LocalVariable lv = (LocalVariable) v;
if (lv.getScopeDepth() < parentScopeDepth && !tmp.get(problem.getDFVar(v))) {
undefinedVars.add(!lv.isOuterScopeVar() ? lv : lv.cloneForDepth(0));
}
tmp.set(problem.getDFVar(lv));
} else if (v instanceof TemporaryLocalVariable) {
TemporaryLocalVariable tlv = (TemporaryLocalVariable) v;
if (!tmp.get(problem.getDFVar(v))) {
undefinedVars.add(tlv);
}
tmp.set(problem.getDFVar(tlv));
}
}
}
if (i instanceof ClosureAcceptingInstr) {
Operand o = ((ClosureAcceptingInstr)i).getClosureArg();
if (o != null && o instanceof WrappedIRClosure) {
identifyUndefinedVarsInClosure(undefinedVars, ((WrappedIRClosure)o).getClosure(), 0);
}
}
if (i instanceof ResultInstr) {
tmp.set(problem.getDFVar(((ResultInstr) i).getResult()));
}
}
}
@Override
public boolean solutionChanged() {
return !tmp.equals(out);
}
@Override
public void finalizeSolution() {
out = tmp;
}
private String printSet(BitSet set) {
StringBuilder buf = new StringBuilder();
int count = 0;
for (int i = 0; i < set.size(); i++) {
if (set.get(i)) {
count++;
buf.append(' ').append(problem.getVariable(i));
if (count % 10 == 0) buf.append("\t\n");
}
}
if (count % 10 != 0) buf.append("\t\t");
return buf.append('\n').toString();
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("\tVars defined on Entry: ");
if (in == null) {
System.out.println("-- NO in!");
} else {
buf.append(printSet(in));
}
buf.append("\n\tVars defined on Exit: ");
if (out == null) {
System.out.println("-- NO out!");
} else {
buf.append(printSet(out));
}
return buf.append('\n').toString();
}
private BitSet in;
private BitSet out;
private BitSet tmp;
private int setSize;
}