StoreLocalVarPlacementProblem slvpp = (StoreLocalVarPlacementProblem)scope.getDataFlowSolution(StoreLocalVarPlacementProblem.NAME);
boolean scopeHasLocalVarStores = false;
boolean scopeHasUnrescuedExceptions = false;
CFG cfg = scope.cfg();
BasicBlock geb = cfg.getGlobalEnsureBB();
if (slvpp != null) {
scopeHasLocalVarStores = slvpp.scopeHasLocalVarStores();
scopeHasUnrescuedExceptions = slvpp.scopeHasUnrescuedExceptions();
} else {
// We dont require local-var load/stores to have been run.
// If it is not run, we go conservative and add push/pop binding instrs. everywhere
scopeHasLocalVarStores = true;
scopeHasUnrescuedExceptions = false;
for (BasicBlock bb: cfg.getBasicBlocks()) {
// SSS FIXME: This is highly conservative. If the bb has an exception raising instr.
// and if we dont have a rescuer, only then do we have unrescued exceptions.
if (cfg.getRescuerBBFor(bb) == null) {
scopeHasUnrescuedExceptions = true;
break;
}
}
}
BasicBlock entryBB = cfg.getEntryBB();
// SSS FIXME: Right now, we always add push/pop frame instrs -- in the future, we may skip them
// for certain scopes.
//
// Add explicit frame and binding push/pop instrs ONLY for methods -- we cannot handle this in closures and evals yet
// If the scope uses $_ or $~ family of vars, has local load/stores, or if its binding has escaped, we have
// to allocate a dynamic scope for it and add binding push/pop instructions.
if ((scope instanceof IRMethod) || (scope instanceof IRScriptBody) || (scope instanceof IRModuleBody)) {
if (scope.bindingHasEscaped() || scope.usesBackrefOrLastline() || scopeHasLocalVarStores || scopeHasUnrescuedExceptions) {
// Push
entryBB.addInstr(new PushFrameInstr());
entryBB.addInstr(new PushBindingInstr(scope));
// Allocate GEB if necessary for popping binding
if (geb == null && (scopeHasLocalVarStores || scopeHasUnrescuedExceptions)) {
Variable exc = scope.getNewTemporaryVariable();
geb = new BasicBlock(cfg, new Label("_GLOBAL_ENSURE_BLOCK"));
geb.addInstr(new ReceiveExceptionInstr(exc, false)); // No need to check type since it is not used before rethrowing
geb.addInstr(new ThrowExceptionInstr(exc));
cfg.addGlobalEnsureBB(geb);
}
// Pop on all scope-exit paths
BasicBlock exitBB = cfg.getExitBB();
for (BasicBlock bb: cfg.getBasicBlocks()) {
ListIterator<Instr> instrs = bb.getInstrs().listIterator();
while (instrs.hasNext()) {
Instr i = instrs.next();
if ((bb != exitBB) && (i instanceof ReturnBase) || (i instanceof BreakInstr)) {
// Add before the break/return