package org.jruby.ir.interpreter;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jruby.ir.IRClosure;
import org.jruby.ir.IRFlags;
import org.jruby.ir.IRScope;
import org.jruby.ir.IRScopeType;
import org.jruby.ir.dataflow.DataFlowProblem;
import org.jruby.ir.instructions.Instr;
import org.jruby.ir.instructions.LabelInstr;
import org.jruby.ir.instructions.ReceiveSelfInstr;
import org.jruby.ir.instructions.ResultInstr;
import org.jruby.ir.instructions.Site;
import org.jruby.ir.operands.Fixnum;
import org.jruby.ir.operands.LocalVariable;
import org.jruby.ir.operands.TemporaryBooleanVariable;
import org.jruby.ir.operands.TemporaryClosureVariable;
import org.jruby.ir.operands.TemporaryFixnumVariable;
import org.jruby.ir.operands.TemporaryFloatVariable;
import org.jruby.ir.operands.TemporaryLocalReplacementVariable;
import org.jruby.ir.operands.TemporaryLocalVariable;
import org.jruby.ir.operands.TemporaryVariable;
import org.jruby.ir.operands.TemporaryVariableType;
import org.jruby.ir.operands.Variable;
import org.jruby.ir.passes.CompilerPass;
import org.jruby.ir.representations.BasicBlock;
import org.jruby.ir.representations.CFG;
import org.jruby.ir.representations.CFGLinearizer;
import org.jruby.ir.transformations.inlining.SimpleCloneInfo;
import static org.jruby.ir.IRFlags.BINDING_HAS_ESCAPED;
public class FullInterpreterContext extends InterpreterContext {
private CFG cfg;
private BasicBlock[] linearizedBBList = null;
private Map<String, DataFlowProblem> dataFlowProblems;
private List<CompilerPass> executedPasses = new ArrayList<>();
private Set<LocalVariable> definedLocalVars;
private Set<LocalVariable> usedLocalVars;
public int floatVariableIndex = -1;
public int fixnumVariableIndex = -1;
public int booleanVariableIndex = -1;
public FullInterpreterContext(IRScope scope, CFG cfg, BasicBlock[] linearizedBBList, int temporaryVariableCount, EnumSet<IRFlags> flags) {
super(scope, (List<Instr>) null, temporaryVariableCount, flags);
this.cfg = cfg;
this.linearizedBBList = linearizedBBList;
}
public FullInterpreterContext(IRScope scope, Instr[] instructions, int temporaryVariableCount, EnumSet<IRFlags> flags) {
super(scope, (List<Instr>)null, temporaryVariableCount, flags);
cfg = buildCFG(instructions);
}
public BasicBlock[] linearizeBasicBlocks() {
linearizedBBList = CFGLinearizer.linearize(cfg);
return linearizedBBList;
}
private CFG buildCFG(Instr[] instructions) {
CFG newCFG = new CFG(getScope());
newCFG.build(instructions);
return newCFG;
}
@Override
public boolean hasExplicitCallProtocol() {
return hasExplicitCallProtocol;
}
public boolean needsBinding() {
return reuseParentDynScope() || !isDynamicScopeEliminated();
}
public void generateInstructionsForInterpretation() {
linearizeBasicBlocks();
List<Instr> newInstrs = new ArrayList<>();
int ipc = 0;
for (BasicBlock b: getLinearizedBBList()) {
b.getLabel().setTargetPC(ipc);
List<Instr> bbInstrs = b.getInstrs();
int bbInstrsLength = bbInstrs.size();
for (int i = 0; i < bbInstrsLength; i++) {
Instr instr = bbInstrs.get(i);
if (!(instr instanceof ReceiveSelfInstr)) {
if (instr instanceof LabelInstr) ((LabelInstr) instr).getLabel().setTargetPC(ipc);
newInstrs.add(instr);
ipc++;
}
}
}
cfg.getExitBB().getLabel().setTargetPC(ipc + 1);
Instr[] linearizedInstrArray = newInstrs.toArray(new Instr[newInstrs.size()]);
BasicBlock[] basicBlocks = getLinearizedBBList();
rescueIPCs = new int[2 * basicBlocks.length];
ipc = 0;
for (int i = 0; i < basicBlocks.length; i++) {
BasicBlock bb = basicBlocks[i];
BasicBlock rescuerBB = cfg.getRescuerBBFor(bb);
int rescuerPC = rescuerBB == null ? -1 : rescuerBB.getLabel().getTargetPC();
rescueIPCs[i * 2] = ipc + bb.getInstrs().size();
rescueIPCs[i * 2 + 1] = rescuerPC;
for (Instr instr : bb.getInstrs()) {
if (!(instr instanceof ReceiveSelfInstr)) {
ipc++;
} else {
rescueIPCs[i * 2]--;
}
}
}
instructions = linearizedInstrArray;
}
@Override
public CFG getCFG() {
return cfg;
}
@Override
public void computeScopeFlagsFromInstructions() {
for (BasicBlock b: cfg.getBasicBlocks()) {
for (Instr i: b.getInstrs()) {
i.computeScopeFlags(getScope(), getFlags());
}
}
}
public Map<String, DataFlowProblem> getDataFlowProblems() {
if (dataFlowProblems == null) dataFlowProblems = new HashMap<>();
return dataFlowProblems;
}
public List<CompilerPass> getExecutedPasses() {
return executedPasses;
}
public BasicBlock[] getLinearizedBBList() {
return linearizedBBList;
}
@Override
public String toStringInstrs() {
return "\nCFG:\n" + cfg.toStringGraph() + "\nInstructions:\n" + cfg.toStringInstrs();
}
public String toStringLinearized() {
StringBuilder buf = new StringBuilder();
for (BasicBlock bb: getLinearizedBBList()) {
buf.append(bb + bb.toStringInstrs());
}
return buf.toString();
}
public FullInterpreterContext duplicate() {
try {
CFG newCFG = cfg.clone(new SimpleCloneInfo(getScope(), false, true), getScope());
BasicBlock[] newLinearizedBBList = new BasicBlock[linearizedBBList.length];
for (int i = 0; i < linearizedBBList.length; i++) {
newLinearizedBBList[i] = newCFG.getBBForLabel(linearizedBBList[i].getLabel());
}
return new FullInterpreterContext(getScope(), newCFG, newLinearizedBBList, temporaryVariableCount, getFlags());
} catch (Throwable t) {
t.printStackTrace();
return null;
}
}
public int determineRPC(int ipc) {
int length = rescueIPCs.length;
for (int i = 0; i + 1 < length; i += 2) {
if (ipc <= rescueIPCs[i]) return rescueIPCs[i + 1];
}
throw new RuntimeException("BUG: no RPC found for " + getFileName() + ":" + getName() + ":" + ipc);
}
public BasicBlock findBasicBlockOf(long callsiteId) {
for (BasicBlock basicBlock: linearizeBasicBlocks()) {
for (Instr instr: basicBlock.getInstrs()) {
if (instr instanceof Site) {
Site site = (Site) instr;
if (site.getCallSiteId() == callsiteId) return basicBlock;
}
}
}
throw new RuntimeException("Bug: Looking for callsiteId: " + callsiteId + " in " + this);
}
public Set<LocalVariable> getUsedLocalVariables() {
return usedLocalVars;
}
public void setUpUseDefLocalVarMaps() {
definedLocalVars = new HashSet<>(1);
usedLocalVars = new HashSet<>(1);
for (BasicBlock bb : getCFG().getBasicBlocks()) {
for (Instr i : bb.getInstrs()) {
for (Variable v : i.getUsedVariables()) {
if (v instanceof LocalVariable) usedLocalVars.add((LocalVariable) v);
}
if (i instanceof ResultInstr) {
Variable v = ((ResultInstr) i).getResult();
if (v instanceof LocalVariable && !((LocalVariable)v).isOuterScopeVar()) {
definedLocalVars.add((LocalVariable) v);
}
}
}
}
for (IRClosure cl : getScope().getClosures()) {
cl.getFullInterpreterContext().setUpUseDefLocalVarMaps();
}
}
public boolean usesLocalVariable(Variable v) {
if (usedLocalVars == null) setUpUseDefLocalVarMaps();
if (usedLocalVars.contains(v)) return true;
for (IRClosure cl : getScope().getClosures()) {
if (cl.getFullInterpreterContext().usesLocalVariable(v)) return true;
}
return false;
}
public boolean definesLocalVariable(Variable v) {
if (definedLocalVars == null) setUpUseDefLocalVarMaps();
if (definedLocalVars.contains(v)) return true;
for (IRClosure cl : getScope().getClosures()) {
if (cl.getFullInterpreterContext().definesLocalVariable(v)) return true;
}
return false;
}
protected void initialize() {
}
public TemporaryVariable createTemporaryVariable() {
temporaryVariableCount++;
if (getScope().getScopeType() == IRScopeType.CLOSURE) {
return new TemporaryClosureVariable(((IRClosure) getScope()).closureId, temporaryVariableCount - 1);
} else {
return getScope().getManager().newTemporaryLocalVariable(temporaryVariableCount - 1);
}
}
public TemporaryLocalVariable getNewTemporaryVariableFor(LocalVariable var) {
temporaryVariableCount++;
return new TemporaryLocalReplacementVariable(var.getId(), temporaryVariableCount - 1);
}
public TemporaryLocalVariable getNewUnboxedVariable(Class type) {
TemporaryVariableType varType;
if (type == Float.class) {
varType = TemporaryVariableType.FLOAT;
} else if (type == Fixnum.class) {
varType = TemporaryVariableType.FIXNUM;
} else if (type == java.lang.Boolean.class) {
varType = TemporaryVariableType.BOOLEAN;
} else {
varType = TemporaryVariableType.LOCAL;
}
return getNewTemporaryVariable(varType);
}
public TemporaryLocalVariable getNewTemporaryVariable(TemporaryVariableType type) {
switch (type) {
case FLOAT: {
floatVariableIndex++;
return new TemporaryFloatVariable(floatVariableIndex);
}
case FIXNUM: {
fixnumVariableIndex++;
return new TemporaryFixnumVariable(fixnumVariableIndex);
}
case BOOLEAN: {
booleanVariableIndex++;
return new TemporaryBooleanVariable(booleanVariableIndex);
}
case LOCAL: {
return getScope().getManager().newTemporaryLocalVariable(temporaryVariableCount - 1);
}
}
throw new RuntimeException("Invalid temporary variable being alloced in this scope: " + type);
}
public boolean needsFrame() {
boolean bindingHasEscaped = bindingHasEscaped();
boolean requireFrame = bindingHasEscaped || getScope().usesEval() || getScope().usesZSuper() || getScope().canCaptureCallersBinding();
for (IRFlags flag : getFlags()) {
switch (flag) {
case BINDING_HAS_ESCAPED:
case REQUIRES_LASTLINE:
case REQUIRES_BACKREF:
case REQUIRES_VISIBILITY:
case REQUIRES_BLOCK:
case REQUIRES_SELF:
case REQUIRES_METHODNAME:
case REQUIRES_CLASS:
requireFrame = true;
}
}
return requireFrame;
}
public boolean bindingHasEscaped() {
return getFlags().contains(BINDING_HAS_ESCAPED);
}
public boolean needsOnlyBackref() {
if (getScope().usesEval() || getScope().usesZSuper() || getScope().canCaptureCallersBinding()) return false;
boolean backrefSeen = false;
for (IRFlags flag : getFlags()) {
switch (flag) {
case BINDING_HAS_ESCAPED:
case REQUIRES_LASTLINE:
case REQUIRES_VISIBILITY:
case REQUIRES_BLOCK:
case REQUIRES_SELF:
case REQUIRES_METHODNAME:
case REQUIRES_CLASS:
return false;
case REQUIRES_BACKREF:
backrefSeen = true;
break;
}
}
return backrefSeen;
}
}