BreakInstr.java

  1. package org.jruby.ir.instructions;

  2. import org.jruby.ir.IRFlags;
  3. import org.jruby.ir.IRScope;
  4. import org.jruby.ir.IRVisitor;
  5. import org.jruby.ir.Operation;
  6. import org.jruby.ir.operands.Operand;
  7. import org.jruby.ir.operands.StringLiteral;
  8. import org.jruby.ir.operands.Variable;
  9. import org.jruby.ir.transformations.inlining.CloneInfo;
  10. import org.jruby.ir.transformations.inlining.InlineCloneInfo;
  11. import org.jruby.ir.transformations.inlining.SimpleCloneInfo;

  12. import java.util.Map;

  13. // NOTE: breaks that jump out of while/until loops would have
  14. // been transformed by the IR building into an ordinary jump.
  15. //
  16. // A break instruction is not just any old instruction.
  17. // Like a return instruction, it exits a scope and returns a value
  18. //
  19. // Ex: (1..5).collect { |n| break if n > 3; n } returns nil
  20. //
  21. // All break instructions like returns have an associated return value
  22. // In the absence of an explicit value to return, nil is returned
  23. //
  24. // Ex: (1..5).collect { |n| break "Hurrah" if n > 3; n } returns "Hurrah"
  25. //
  26. // But, whereas a return exits the innermost method it is in,
  27. // a break only exits out of the innermost non-method scope it is in.
  28. // So, an exposed/naked break inside a method throws an exception!
  29. //
  30. // def foo(n); break if n > 5; end; foo(100) will throw an exception
  31. //
  32. public class BreakInstr extends Instr implements FixedArityInstr {
  33.     private final String scopeName; // Primarily a debugging aid
  34.     private Operand returnValue;

  35.     public BreakInstr(Operand rv, String scopeName) {
  36.         super(Operation.BREAK);
  37.         this.scopeName = scopeName;
  38.         this.returnValue = rv;
  39.     }

  40.     public String getScopeName() {
  41.         return scopeName;
  42.     }

  43.     public Operand getReturnValue() {
  44.         return returnValue;
  45.     }

  46.     @Override
  47.     public Operand[] getOperands() {
  48.         return new Operand[] { new StringLiteral(scopeName), returnValue };
  49.     }

  50.     @Override
  51.     public boolean computeScopeFlags(IRScope scope) {
  52.         scope.getFlags().add(IRFlags.HAS_BREAK_INSTRS);
  53.         scope.getFlags().add(IRFlags.REQUIRES_DYNSCOPE);
  54.         return true;
  55.     }

  56.     @Override
  57.     public Instr clone(CloneInfo info) {
  58.         if (info instanceof SimpleCloneInfo) return new BreakInstr(returnValue.cloneForInlining(info), scopeName);

  59.         InlineCloneInfo ii = (InlineCloneInfo) info;

  60.         if (ii.isClosure()) {
  61.             // SSS FIXME: This is buggy!
  62.             //
  63.             // If scopeIdToReturnTo is a closure, it could have
  64.             // been cloned as well!! This is only an issue if we
  65.             // inline in closures. But, if we always inline in methods,
  66.             // this will continue to work.
  67.             //
  68.             // Hmm ... we need to figure out the required inlining info here.
  69.             //
  70.             // if (ii.getHostScope().getScopeId() == scopeIdToReturnTo) {
  71.             //
  72.             if (false) {
  73.                 // If the break got inlined into the scope we had to break to, replace the break
  74.                 // with a COPY of the break-value into the call's result var.
  75.                 // Ex: v = foo { ..; break n; ..}.  So, "break n" is replaced with "v = n"
  76.                 // The CFG for the closure will be such that after break, control goes to the
  77.                 // scope exit block.  So, we know that after the copy, we'll continue with the
  78.                 // instruction after the call.
  79.                 Variable v = ii.getCallResultVariable();
  80.                 return (v == null) ? null : new CopyInstr(v, returnValue.cloneForInlining(ii));
  81.             }

  82.             return new BreakInstr(returnValue.cloneForInlining(ii), scopeName);
  83.         } else {
  84.             throw new UnsupportedOperationException("Break instructions shouldn't show up outside closures.");
  85.         }
  86.     }

  87.     @Override
  88.     public String toString() {
  89.         return getOperation() + "(" + returnValue + ", " + scopeName + ")";
  90.     }

  91.     @Override
  92.     public void simplifyOperands(Map<Operand, Operand> valueMap, boolean force) {
  93.         returnValue = returnValue.getSimplifiedOperand(valueMap, force);
  94.     }

  95.     @Override
  96.     public void visit(IRVisitor visitor) {
  97.         visitor.BreakInstr(this);
  98.     }
  99. }