Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    ***** BEGIN LICENSE BLOCK *****
    * Version: EPL 1.0/GPL 2.0/LGPL 2.1
    *
    * The contents of this file are subject to the Eclipse Public
    * License Version 1.0 (the "License"); you may not use this file
    * except in compliance with the License. You may obtain a copy of
    * the License at http://www.eclipse.org/legal/epl-v10.html
    *
   * Software distributed under the License is distributed on an "AS
   * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
   * implied. See the License for the specific language governing
   * rights and limitations under the License.
   *
   * Copyright (C) 2006 Charles O Nutter <headius@headius.com>
   *  
   * Alternatively, the contents of this file may be used under the terms of
   * either of the GNU General Public License Version 2 or later (the "GPL"),
   * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
   * in which case the provisions of the GPL or the LGPL are applicable instead
   * of those above. If you wish to allow use of your version of this file only
   * under the terms of either the GPL or the LGPL, and not to allow others to
   * use your version of this file under the terms of the EPL, indicate your
   * decision by deleting the provisions above and replace them with the notice
   * and other provisions required by the GPL or the LGPL. If you do not delete
   * the provisions above, a recipient may use your version of this file under
   * the terms of any one of the EPL, the GPL or the LGPL.
   ***** END LICENSE BLOCK *****/
  
  package org.jruby.compiler;
  
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import org.jruby.ast.*;

Author(s):
headius
  
  public class ASTCompiler {
      private boolean isAtRoot = true;
  
      protected boolean is1_9() {
          return false;
      }
  
      public void compileBody(Node nodeBodyCompiler contextboolean expr) {
          Node oldBodyNode = ;
           = node;
          compile(nodecontextexpr);
           = oldBodyNode;
      }
      
      public void compile(Node nodeBodyCompiler contextboolean expr) {
          if (node == null) {
              if (exprcontext.loadNil();
              return;
          }
          switch (node.getNodeType()) {
              case :
                  compileAlias((AliasNodenodecontextexpr);
                  break;
              case :
                  compileAnd(nodecontextexpr);
                  break;
              case :
                  compileArgsCat(nodecontextexpr);
                  break;
              case :
                  compileArgsPush(nodecontextexpr);
                  break;
              case :
                  compileArray(nodecontextexpr);
                  break;
              case :
                  compileAttrAssign(nodecontextexpr);
                  break;
              case :
                  compileBackref(nodecontextexpr);
                  break;
             case :
                 compileBegin(nodecontextexpr);
                 break;
             case :
                 compileBignum(nodecontextexpr);
                 break;
             case :
                 compileBlock(nodecontextexpr);
                 break;
             case :
                 compileBreak(nodecontextexpr);
                 break;
             case :
                 compileCall(nodecontextexpr);
                 break;
             case :
                 compileCase(nodecontextexpr);
                 break;
             case :
                 compileClass(nodecontextexpr);
                 break;
             case :
                 compileClassVar(nodecontextexpr);
                 break;
             case :
                 compileClassVarAsgn(nodecontextexpr);
                 break;
             case :
                 compileClassVarDecl(nodecontextexpr);
                 break;
             case :
                 compileColon2(nodecontextexpr);
                 break;
             case :
                 compileColon3(nodecontextexpr);
                 break;
             case :
                 compileConstDecl(nodecontextexpr);
                 break;
             case :
                 compileConst(nodecontextexpr);
                 break;
             case :
                 compileDAsgn(nodecontextexpr);
                 break;
             case :
                 compileDefined(nodecontextexpr);
                 break;
             case :
                 compileDefn(nodecontextexpr);
                 break;
             case :
                 compileDefs(nodecontextexpr);
                 break;
             case :
                 compileDot(nodecontextexpr);
                 break;
             case :
                 compileDRegexp(nodecontextexpr);
                 break;
             case :
                 compileDStr(nodecontextexpr);
                 break;
             case :
                 compileDSymbol(nodecontextexpr);
                 break;
             case :
                 compileDVar(nodecontextexpr);
                 break;
             case :
                 compileDXStr(nodecontextexpr);
                 break;
             case :
                 compileEnsureNode(nodecontextexpr);
                 break;
             case :
                 compileEvStr(nodecontextexpr);
                 break;
             case :
                 compileFalse(nodecontextexpr);
                 break;
             case :
                 compileFCall(nodecontextexpr);
                 break;
             case :
                 compileFixnum(nodecontextexpr);
                 break;
             case :
                 compileFlip(nodecontextexpr);
                 break;
             case :
                 compileFloat(nodecontextexpr);
                 break;
             case :
                 compileFor(nodecontextexpr);
                 break;
             case :
                 compileGlobalAsgn(nodecontextexpr);
                 break;
             case :
                 compileGlobalVar(nodecontextexpr);
                 break;
             case :
                 compileHash(nodecontextexpr);
                 break;
             case :
                 compileIf(nodecontextexpr);
                 break;
             case :
                 compileInstAsgn(nodecontextexpr);
                 break;
             case :
                 compileInstVar(nodecontextexpr);
                 break;
             case :
                 compileIter(nodecontext);
                 break;
             case :
                 compileLiteral((LiteralNodenodecontext);
                 break;
             case :
                 compileLocalAsgn(nodecontextexpr);
                 break;
             case :
                 compileLocalVar(nodecontextexpr);
                 break;
             case :
                 compileMatch2(nodecontextexpr);
                 break;
             case :
                 compileMatch3(nodecontextexpr);
                 break;
             case :
                 compileMatch(nodecontextexpr);
                 break;
             case :
                 compileModule(nodecontextexpr);
                 break;
             case :
                 compileMultipleAsgn(nodecontextexpr);
                 break;
             case :
                 compileNewline(nodecontextexpr);
                 break;
             case :
                 compileNext(nodecontextexpr);
                 break;
             case :
                 compileNthRef(nodecontextexpr);
                 break;
             case :
                 compileNil(nodecontextexpr);
                 break;
             case :
                 compileNot(nodecontextexpr);
                 break;
             case :
                 compileOpAsgnAnd(nodecontextexpr);
                 break;
             case :
                 compileOpAsgn(nodecontextexpr);
                 break;
             case :
                 compileOpAsgnOr(nodecontextexpr);
                 break;
             case :
                 compileOpElementAsgn(nodecontextexpr);
                 break;
             case :
                 compileOr(nodecontextexpr);
                 break;
             case :
                 compilePostExe(nodecontextexpr);
                 break;
             case :
                 compilePreExe(nodecontextexpr);
                 break;
             case :
                 compileRedo(nodecontextexpr);
                 break;
             case :
                 compileRegexp(nodecontextexpr);
                 break;
             case :
                 throw new NotCompilableException("rescue body is handled by rescue compilation at: " + node.getPosition());
             case :
                 compileRescue(nodecontextexpr);
                 break;
             case :
                 compileRetry(nodecontextexpr);
                 break;
             case :
                 compileReturn(nodecontextexpr);
                 break;
             case :
                 throw new NotCompilableException("Use compileRoot(); Root node at: " + node.getPosition());
             case :
                 compileSClass(nodecontextexpr);
                 break;
             case :
                 compileSelf(nodecontextexpr);
                 break;
             case :
                 compileSplat(nodecontextexpr);
                 break;
             case :
                 compileStr(nodecontextexpr);
                 break;
             case :
                 compileSuper(nodecontextexpr);
                 break;
             case :
                 compileSValue(nodecontextexpr);
                 break;
             case :
                 compileSymbol(nodecontextexpr);
                 break;
             case :
                 compileToAry(nodecontextexpr);
                 break;
             case :
                 compileTrue(nodecontextexpr);
                 break;
             case :
                 compileUndef((UndefNodenodecontextexpr);
                 break;
             case :
                 compileUntil(nodecontextexpr);
                 break;
             case :
                 compileVAlias(nodecontextexpr);
                 break;
             case :
                 compileVCall(nodecontextexpr);
                 break;
             case :
                 compileWhile(nodecontextexpr);
                 break;
             case :
                 assert false : "When nodes are handled by case node compilation.";
                 break;
             case :
                 compileXStr(nodecontextexpr);
                 break;
             case :
                 compileYield(nodecontextexpr);
                 break;
             case :
                 compileZArray(nodecontextexpr);
                 break;
             case :
                 compileZSuper(nodecontextexpr);
                 break;
             default:
                 throw new NotCompilableException("Unknown node encountered in compiler: " + node);
         }
     }
 
     public void compileArguments(Node nodeBodyCompiler context) {
         switch (node.getNodeType()) {
             case :
                 compileArgsCatArguments(nodecontexttrue);
                 break;
             case :
                 compileArgsPushArguments(nodecontexttrue);
                 break;
             case :
                 compileArrayArguments(nodecontexttrue);
                 break;
             case :
                 compileSplatArguments(nodecontexttrue);
                 break;
             default:
                 compile(nodecontexttrue);
                 context.convertToJavaArray();
         }
     }
     
     public class VariableArityArguments implements ArgumentsCallback {
         private Node node;
         
         public VariableArityArguments(Node node) {
             this. = node;
         }
         
         public int getArity() {
             return -1;
         }
         
         public void call(BodyCompiler context) {
             compileArguments(context);
         }
     }
     
     public class SpecificArityArguments implements ArgumentsCallback {
         private int arity;
         private Node node;
         
         public SpecificArityArguments(Node node) {
             if (node.getNodeType() == . && ((ArrayNode)node).isLightweight()) {
                 // only arrays that are "lightweight" are being used as args arrays
                 this. = ((ArrayNode)node).size();
             } else {
                 // otherwise, it's a literal array
                 this. = 1;
             }
             this. = node;
         }
         
         public int getArity() {
             return ;
         }
         
         public void call(BodyCompiler context) {
             if (.getNodeType() == .) {
                 ArrayNode arrayNode = (ArrayNode);
                 if (arrayNode.isLightweight()) {
                     // explode array, it's an internal "args" array
                     for (Node n : arrayNode.childNodes()) {
                         compile(ncontext,true);
                     }
                 } else {
                     // use array as-is, it's a literal array
                     compile(arrayNodecontext,true);
                 }
             } else {
                 compile(context,true);
             }
         }
     }
 
     public ArgumentsCallback getArgsCallback(Node node) {
         if (node == null) {
             return null;
         }
         // unwrap newline nodes to get their actual type
         while (node.getNodeType() == .) {
             node = ((NewlineNode)node).getNextNode();
         }
         switch (node.getNodeType()) {
             case :
             case :
             case :
                 return new VariableArityArguments(node);
             case :
                 ArrayNode arrayNode = (ArrayNode)node;
                 if (arrayNode.size() == 0) {
                     return null;
                 } else if (arrayNode.size() > 3) {
                     return new VariableArityArguments(node);
                 } else {
                     return new SpecificArityArguments(node);
                 }
             default:
                 return new SpecificArityArguments(node);
         }
     }
 
     public void compileAssignment(Node nodeBodyCompiler context) {
         switch (node.getNodeType()) {
             case :
                 compileAttrAssignAssignment(nodecontext);
                 break;
             case :
                 DAsgnNode dasgnNode = (DAsgnNode)node;
                 context.getVariableCompiler().assignLocalVariable(dasgnNode.getIndex(), dasgnNode.getDepth(), false);
                 break;
             case :
                 compileClassVarAsgnAssignment(nodecontext);
                 break;
             case :
                 compileClassVarDeclAssignment(nodecontext);
                 break;
             case :
                 compileConstDeclAssignment(nodecontext);
                 break;
             case :
                 compileGlobalAsgnAssignment(nodecontext);
                 break;
             case :
                 compileInstAsgnAssignment(nodecontext);
                 break;
             case :
                 LocalAsgnNode localAsgnNode = (LocalAsgnNode)node;
                 context.getVariableCompiler().assignLocalVariable(localAsgnNode.getIndex(), localAsgnNode.getDepth(), false);
                 break;
             case :
                 compileMultipleAsgnAssignment(nodecontextfalse);
                 break;
             case :
                 context.consumeCurrentValue();
                 break;
             default:
                 throw new NotCompilableException("Can't compile assignment node: " + node);
         }
     }
 
     public void compileAlias(final AliasNode aliasBodyCompiler contextboolean expr) {
         CompilerCallback args = new CompilerCallback() {
             public void call(BodyCompiler context) {
                 compile(alias.getNewName(), contexttrue);
                 compile(alias.getOldName(), contexttrue);
             }
         };
 
         context.defineAlias(args);
         
         // TODO: don't require pop
         if (!exprcontext.consumeCurrentValue();
     }
 
     public void compileAnd(Node nodeBodyCompiler contextfinal boolean expr) {
         final AndNode andNode = (AndNodenode;
 
         if (andNode.getFirstNode().getNodeType().alwaysTrue()) {
             // compile first node as non-expr and then second node
             compile(andNode.getFirstNode(), contextfalse);
             compile(andNode.getSecondNode(), contextexpr);
         } else if (andNode.getFirstNode().getNodeType().alwaysFalse()) {
             // compile first node only
             compile(andNode.getFirstNode(), contextexpr);
         } else {
             compile(andNode.getFirstNode(), contexttrue);
             BranchCallback longCallback = new BranchCallback() {
                         public void branch(BodyCompiler context) {
                             compile(andNode.getSecondNode(), contexttrue);
                         }
                     };
 
             context.performLogicalAnd(longCallback);
             if (!exprcontext.consumeCurrentValue();
         }
     }
 
     public void compileArray(Node nodeBodyCompiler contextboolean expr) {
         ArrayNode arrayNode = (ArrayNodenode;
         
         if (expr) {
             ArrayCallback callback = new ArrayCallback() {
                 public void nextValue(BodyCompiler contextObject sourceArrayint index) {
                     Node node = (Node) ((Object[]) sourceArray)[index];
                     compile(nodecontexttrue);
                 }
             };
 
             List<NodechildNodes = arrayNode.childNodes();
 
             if (isListAllLiterals(arrayNode)) {
                 context.createNewLiteralArray(childNodes.toArray(), callbackarrayNode.isLightweight());
             } else {
                 context.createNewArray(childNodes.toArray(), callbackarrayNode.isLightweight());
             }
         } else {
             if (isListAllLiterals(arrayNode)) {
                 // do nothing, no observable effect
             } else {
                 for (Iterator<Nodeiter = arrayNode.childNodes().iterator(); iter.hasNext();) {
                     Node nextNode = iter.next();
                     compile(nextNodecontextfalse);
                 }
             }
         }
     }
 
     private boolean isListAllLiterals(ListNode listNode) {
         for (int i = 0; i < listNode.size(); i++) {
             switch (listNode.get(i).getNodeType()) {
             case :
             case :
             case :
             case :
             case :
             case :
             case :
             case :
                 // simple literals, continue
                 continue;
             case :
                 // scan contained array
                 if (isListAllLiterals((ArrayNode)listNode.get(i))) {
                     continue;
                 } else {
                     return false;
                 }
             case :
                 // scan contained hash
                 if (isListAllLiterals(((HashNode)listNode.get(i)).getListNode())) {
                     continue;
                 } else {
                     return false;
                 }
             default:
                 return false;
             }
         }
 
         // all good!
         return true;
     }
 
     public void compileArgsCat(Node nodeBodyCompiler contextboolean expr) {
         ArgsCatNode argsCatNode = (ArgsCatNodenode;
 
         compile(argsCatNode.getFirstNode(), context,true);
         compile(argsCatNode.getSecondNode(), context,true);
         context.argsCat();
 
         // TODO: don't require pop
         if (!exprcontext.consumeCurrentValue();
     }
 
     public void compileArgsPush(Node nodeBodyCompiler contextboolean expr) {
         throw new NotCompilableException("ArgsPush should never be encountered bare in 1.8");
     }
 
     private void compileAttrAssign(Node nodeBodyCompiler contextboolean expr) {
         final AttrAssignNode attrAssignNode = (AttrAssignNodenode;
 
         CompilerCallback receiverCallback = new CompilerCallback() {
             public void call(BodyCompiler context) {
                 compile(attrAssignNode.getReceiverNode(), context,true);
             }
         };
         
         ArgumentsCallback argsCallback = getArgsCallback(attrAssignNode.getArgsNode());
 
         // Ruby 1.8 and 1.9 only bypass visibility check when receiver is statically
         // determined to be self.
         boolean isSelf = attrAssignNode.getReceiverNode() instanceof SelfNode;
         context.getInvocationCompiler().invokeAttrAssign(attrAssignNode.getName(), receiverCallbackargsCallbackisSelfexpr);
     }
 
     public void compileAttrAssignAssignment(Node nodeBodyCompiler context) {
         final AttrAssignNode attrAssignNode = (AttrAssignNodenode;
 
         CompilerCallback receiverCallback = new CompilerCallback() {
             public void call(BodyCompiler context) {
                 compile(attrAssignNode.getReceiverNode(), context,true);
             }
         };
         ArgumentsCallback argsCallback = getArgsCallback(attrAssignNode.getArgsNode());
 
         // Ruby 1.8 and 1.9 only bypass visibility check when receiver is statically
         // determined to be self.
         boolean isSelf = attrAssignNode.getReceiverNode() instanceof SelfNode;
         
         // this is always treated as non-expr, so it pops for us
         context.getInvocationCompiler().invokeAttrAssignMasgn(attrAssignNode.getName(), receiverCallbackargsCallbackisSelf);
     }
 
     public void compileBackref(Node nodeBodyCompiler contextboolean expr) {
         BackRefNode iVisited = (BackRefNodenode;
 
         context.performBackref(iVisited.getType());
         // TODO: don't require pop
         if (!exprcontext.consumeCurrentValue();
     }
 
     public void compileBegin(Node nodeBodyCompiler contextboolean expr) {
         BeginNode beginNode = (BeginNodenode;
 
         compile(beginNode.getBodyNode(), contextexpr);
     }
 
     public void compileBignum(Node nodeBodyCompiler contextboolean expr) {
         if (exprcontext.createNewBignum(((BignumNodenode).getValue());
     }
 
     public void compileBlock(Node nodeBodyCompiler contextboolean expr) {
         BlockNode blockNode = (BlockNodenode;
 
         for (Iterator<Nodeiter = blockNode.childNodes().iterator(); iter.hasNext();) {
             Node n = iter.next();
 
             compile(ncontextiter.hasNext() ? false : expr);
         }
     }
 
     public void compileBreak(Node nodeBodyCompiler contextboolean expr) {
         final BreakNode breakNode = (BreakNodenode;
 
         CompilerCallback valueCallback = new CompilerCallback() {
 
                     public void call(BodyCompiler context) {
                         if (breakNode.getValueNode() != null) {
                             compile(breakNode.getValueNode(), contexttrue);
                         } else {
                             context.loadNil();
                         }
                     }
                 };
 
         context.issueBreakEvent(valueCallback);
         // TODO: don't require pop
         if (!exprcontext.consumeCurrentValue();
     }
 
     public void compileCall(Node nodeBodyCompiler contextboolean expr) {
         final CallNode callNode = (CallNodenode;
 
         CompilerCallback receiverCallback = new CompilerCallback() {
             public void call(BodyCompiler context) {
                 compile(callNode.getReceiverNode(), contexttrue);
             }
         };
 
         ArgumentsCallback argsCallback = getArgsCallback(callNode.getArgsNode());
         CompilerCallback closureArg = getBlock(callNode.getIterNode());
 
         String name = callNode.getName();
         CallType callType = .;
 
         if (argsCallback != null && argsCallback.getArity() == 1) {
             Node argument = callNode.getArgsNode().childNodes().get(0);
             if (argument instanceof FixnumNode) {
                 if (MethodIndex.hasFastFixnumOps(name)) {
                     context.getInvocationCompiler().invokeBinaryFixnumRHS(namereceiverCallback, ((FixnumNode)argument).getValue());
                     if (!exprcontext.consumeCurrentValue();
                     return;
                 }
             } else if (argument instanceof FloatNode) {
                 if (MethodIndex.hasFastFloatOps(name)) {
                     context.getInvocationCompiler().invokeBinaryFloatRHS(namereceiverCallback, ((FloatNode)argument).getValue());
                     if (!exprcontext.consumeCurrentValue();
                     return;
                 }
             }
         }
 
         // if __send__ with a literal symbol, compile it as a direct fcall
             String literalSend = getLiteralSend(callNode);
             if (literalSend != null) {
                 name = literalSend;
                 callType = .;
             }
         }
         
         if (callNode instanceof SpecialArgs) {
             context.getInvocationCompiler().invokeDynamicVarargs(
                     namereceiverCallbackargsCallback,
                     callTypeclosureArgcallNode.getIterNode() instanceof IterNode);
         } else {
             context.getInvocationCompiler().invokeDynamic(
                     namereceiverCallbackargsCallback,
                     callTypeclosureArgcallNode.getIterNode() instanceof IterNode);
         }
         
         // TODO: don't require pop
         if (!exprcontext.consumeCurrentValue();
     }
 
     private static final Map<ClassMap<ClassMap<StringString>>> Intrinsics;
     static {
          = new HashMap();
 
         Map<ClassMap<StringString>> fixnumIntrinsics = new HashMap();
         .put(RubyFixnum.classfixnumIntrinsics);
 
         Map<StringStringfixnumLongIntrinsics = new HashMap();
         fixnumIntrinsics.put(FixnumNode.classfixnumLongIntrinsics);
         
         fixnumLongIntrinsics.put("+""op_plus");
         fixnumLongIntrinsics.put("-""op_minus");
         fixnumLongIntrinsics.put("/""op_div");
         fixnumLongIntrinsics.put("*""op_plus");
         fixnumLongIntrinsics.put("**""op_pow");
         fixnumLongIntrinsics.put("<""op_lt");
         fixnumLongIntrinsics.put("<=""op_le");
         fixnumLongIntrinsics.put(">""op_gt");
         fixnumLongIntrinsics.put(">=""op_ge");
         fixnumLongIntrinsics.put("==""op_equal");
         fixnumLongIntrinsics.put("<=>""op_cmp");
 
         Map<ClassMap<StringString>> floatIntrinsics = new HashMap();
         .put(RubyFloat.classfloatIntrinsics);
 
         Map<StringString>floatDoubleIntrinsics = new HashMap();
         floatIntrinsics.put(FloatNode.classfloatDoubleIntrinsics);
         
         floatDoubleIntrinsics.put("+""op_plus");
         floatDoubleIntrinsics.put("-""op_minus");
         floatDoubleIntrinsics.put("/""op_fdiv");
         floatDoubleIntrinsics.put("*""op_plus");
         floatDoubleIntrinsics.put("**""op_pow");
         floatDoubleIntrinsics.put("<""op_lt");
         floatDoubleIntrinsics.put("<=""op_le");
         floatDoubleIntrinsics.put(">""op_gt");
         floatDoubleIntrinsics.put(">=""op_ge");
         floatDoubleIntrinsics.put("==""op_equal");
         floatDoubleIntrinsics.put("<=>""op_cmp");
     }
 
     private String getLiteralSend(CallNode callNode) {
         if (callNode.getName().equals("__send__")) {
             if (callNode.getArgsNode() instanceof ArrayNode) {
                 ArrayNode arrayNode = (ArrayNode)callNode.getArgsNode();
                 if (arrayNode.get(0) instanceof SymbolNode) {
                     return ((SymbolNode)arrayNode.get(0)).getName();
                 } else if (arrayNode.get(0) instanceof StrNode) {
                     return ((StrNode)arrayNode.get(0)).getValue().toString();
                 }
             }
         }
         return null;
     }
 
     public void compileCase(Node nodeBodyCompiler contextboolean expr) {
         CaseNode caseNode = (CaseNodenode;
 
         boolean hasCase = caseNode.getCaseNode() != null;
 
         // aggregate when nodes into a list, unfortunately, this is no
         List<Nodecases = caseNode.getCases().childNodes();
 
         // last node, either !instanceof WhenNode or null, is the else
         Node elseNode = caseNode.getElseNode();
 
         compileWhen(caseNode.getCaseNode(), caseselseNodecontextexprhasCase);
     }
 
     private FastSwitchType getHomogeneousSwitchType(List<NodewhenNodes) {
         FastSwitchType foundType = null;
         Outer: for (Node node : whenNodes) {
             WhenNode whenNode = (WhenNode)node;
             if (whenNode.getExpressionNodes() instanceof ArrayNode) {
                 ArrayNode arrayNode = (ArrayNode)whenNode.getExpressionNodes();
 
                 for (Node maybeFixnum : arrayNode.childNodes()) {
                     if (maybeFixnum instanceof FixnumNode) {
                         FixnumNode fixnumNode = (FixnumNode)maybeFixnum;
                         long value = fixnumNode.getValue();
                         if (value <= . && value >= .) {
                             if (foundType != null && foundType != .return null;
                             if (foundType == nullfoundType = .;
                             continue;
                         } else {
                             return null;
                         }
                     } else {
                         return null;
                     }
                 }
             } else if (whenNode.getExpressionNodes() instanceof FixnumNode) {
                 FixnumNode fixnumNode = (FixnumNode)whenNode.getExpressionNodes();
                 long value = fixnumNode.getValue();
                 if (value <= . && value >= .) {
                     if (foundType != null && foundType != .return null;
                     if (foundType == nullfoundType = .;
                     continue;
                 } else {
                     return null;
                 }
             } else if (whenNode.getExpressionNodes() instanceof StrNode) {
                 StrNode strNode = (StrNode)whenNode.getExpressionNodes();
                 if (strNode.getValue().length() == 1) {
                     if (foundType != null && foundType != .return null;
                     if (foundType == nullfoundType = .;
 
                     continue;
                 }
             } else if (whenNode.getExpressionNodes() instanceof SymbolNode) {
                 SymbolNode symbolNode = (SymbolNode)whenNode.getExpressionNodes();
                 if (symbolNode.getName().length() == 1) {
                     if (foundType != null && foundType != .return null;
                     if (foundType == nullfoundType = .;
 
                     continue;
                 }
             } else {
                 return null;
             }
         }
         return foundType;
     }
 
     public void compileWhen(final Node valueList<NodewhenNodesfinal Node elseNodeBodyCompiler contextfinal boolean exprfinal boolean hasCase) {
         CompilerCallback caseValue = null;
         if (value != nullcaseValue = new CompilerCallback() {
             public void call(BodyCompiler context) {
                 compile(valuecontexttrue);
                 context.pollThreadEvents();
             }
         };
 
         List<ArgumentsCallbackconditionals = new ArrayList<ArgumentsCallback>();
         List<CompilerCallbackbodies = new ArrayList<CompilerCallback>();
         Map<CompilerCallbackint[]> switchCases = null;
         FastSwitchType switchType = getHomogeneousSwitchType(whenNodes);
         if (switchType != null && !.) {
             // NOTE: Currently this optimization is limited to the following scenarios:
             // * All expressions are int-ranged literal fixnums
             // * All expressions are single-character literal symbols
             // * All expressions are single-character literal strings
             // If the case value is not of the same type as the when values, the
             // default === logic applies.
             switchCases = new HashMap<CompilerCallbackint[]>();
         }
         for (Node node : whenNodes) {
             final WhenNode whenNode = (WhenNode)node;
             CompilerCallback body = new CompilerCallback() {
                 public void call(BodyCompiler context) {
                     if (.context.traceLine();
                     compile(whenNode.getBodyNode(), contextexpr);
                 }
             };
             addConditionalForWhen(whenNodeconditionalsbodiesbody);
             if (switchCases != nullswitchCases.put(bodygetOptimizedCases(whenNode));
         }
         
         CompilerCallback fallback = new CompilerCallback() {
             public void call(BodyCompiler context) {
                 compile(elseNodecontextexpr);
             }
         };
         
         context.compileSequencedConditional(caseValueswitchTypeswitchCasesconditionalsbodiesfallback);
     }
 
     private int[] getOptimizedCases(WhenNode whenNode) {
         if (whenNode.getExpressionNodes() instanceof ArrayNode) {
             ArrayNode expression = (ArrayNode)whenNode.getExpressionNodes();
             if (expression.get(expression.size() - 1) instanceof WhenNode) {
                 // splatted when, can't do it yet
                 return null;
             }
 
             int[] cases = new int[expression.size()];
             for (int i = 0; i < cases.lengthi++) {
                 switch (expression.get(i).getNodeType()) {
                 case :
                     cases[i] = (int)((FixnumNode)expression.get(i)).getValue();
                     break;
                 default:
                     // can't do it
                     return null;
                 }
             }
             return cases;
         } else if (whenNode.getExpressionNodes() instanceof FixnumNode) {
             FixnumNode fixnumNode = (FixnumNode)whenNode.getExpressionNodes();
             return new int[] {(int)fixnumNode.getValue()};
         } else if (whenNode.getExpressionNodes() instanceof StrNode) {
             StrNode strNode = (StrNode)whenNode.getExpressionNodes();
             if (strNode.getValue().length() == 1) {
                 return new int[] {strNode.getValue().get(0)};
             } else {
                 return new int[] {strNode.getValue().hashCode()};
             }
         } else if (whenNode.getExpressionNodes() instanceof SymbolNode) {
             SymbolNode symbolNode = (SymbolNode)whenNode.getExpressionNodes();
             if (symbolNode.getName().length() == 1) {
                 return new int[] {symbolNode.getName().charAt(0)};
             } else {
                 return new int[] {symbolNode.getName().hashCode()};
             }
         }
         return null;
     }
 
     private void addConditionalForWhen(final WhenNode whenNodeList<ArgumentsCallbackconditionalsList<CompilerCallbackbodiesCompilerCallback body) {
         bodies.add(body);
 
         // If it's a single-arg when but contains an array, we know it's a real literal array
         // FIXME: This is a gross way to figure it out; parser help similar to yield argument passing (expandArguments) would be better
         if (whenNode.getExpressionNodes() instanceof ArrayNode) {
             if (whenNode instanceof WhenOneArgNode) {
                 // one arg but it's an array, treat it as a proper array
                 conditionals.add(new ArgumentsCallback() {
                     public int getArity() {
                         return 1;
                     }
 
                     public void call(BodyCompiler context) {
                         compile(whenNode.getExpressionNodes(), contexttrue);
                     }
                 });
                 return;
             }
         }
         // otherwise, use normal args compiler
         conditionals.add(getArgsCallback(whenNode.getExpressionNodes()));
     }
 
     public void compileClass(Node nodeBodyCompiler contextboolean expr) {