Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    **** BEGIN LICENSE BLOCK *****
    * Version: CPL 1.0/GPL 2.0/LGPL 2.1
    *
    * The contents of this file are subject to the Common 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/cpl-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) 2001-2011 The JRuby Community (and contribs)
   *
   * 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 CPL, 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 CPL, the GPL or the LGPL.
   ***** END LICENSE BLOCK *****/
  package org.jruby.runtime.invokedynamic;
  
  
  import static java.lang.invoke.MethodHandles.*;
  import static java.lang.invoke.MethodType.*;
  
  import java.util.Arrays;
  
  import org.jruby.*;
  
  import static java.lang.invoke.MethodType.methodType;
  import static org.jruby.runtime.invokedynamic.InvokeDynamicSupport.*;
  import static org.jruby.util.CodegenUtils.p;

Bootstrapping logic for invokedynamic-based invocation.
  
  public class InvocationLinker {
      private static final Logger LOG = LoggerFactory.getLogger("InvocationLinker");
      
      public static CallSite invocationBootstrap(Lookup lookupString nameMethodType typeString fileint linethrows NoSuchMethodExceptionIllegalAccessException {
          String[] names = name.split(":");
          String operation = names[0];
  
          if (name.equals("yieldSpecific")) {
              MethodHandle target = lookup.findStatic(InvocationLinker.class"yieldSpecificFallback"type);
              return new ConstantCallSite(target);
          }
  
          JRubyCallSite site;
          String method = JavaNameMangler.demangleMethodName(names[1]);
          if (operation.equals("call")) {
              site = new JRubyCallSite(lookuptype.filelinemethodfalsefalsetrue);
          } else if (operation.equals("fcall")) {
              site = new JRubyCallSite(lookuptype.filelinemethodfalsefalsetrue);
          } else if (operation.equals("vcall")) {
              site = new JRubyCallSite(lookuptype.filelinemethodfalsefalsetrue);
          } else if (operation.equals("callIter")) {
              site = new JRubyCallSite(lookuptype.filelinemethodfalsetruetrue);
          } else if (operation.equals("fcallIter")) {
              site = new JRubyCallSite(lookuptype.filelinemethodfalsetruetrue);
          } else if (operation.equals("vcallIter")) {
              site = new JRubyCallSite(lookuptype.filelinemethodfalsetruetrue);
          } else if (operation.equals("attrAssign")) {
              site = new JRubyCallSite(lookuptype.filelinemethodtruefalsefalse);
          } else if (operation.equals("attrAssignSelf")) {
              site = new JRubyCallSite(lookuptype.filelinemethodtruefalsefalse);
          } else if (operation.equals("attrAssignExpr")) {
             site = new JRubyCallSite(lookuptype.filelinemethodtruefalsetrue);
         } else if (operation.equals("attrAssignSelfExpr")) {
             site = new JRubyCallSite(lookuptype.filelinemethodtruefalsetrue);
         } else {
             throw new RuntimeException("wrong invokedynamic target: " + name);
         }
         
         MethodType fallbackType = type.insertParameterTypes(0, JRubyCallSite.class);
         MethodHandle myFallback = insertArguments(
                 lookup.findStatic(InvocationLinker.class"invocationFallback",
                 fallbackType),
                 0,
                 site);
 
         site.setInitialTarget(myFallback);
         return site;
     }
     
     public static IRubyObject invocationFallback(JRubyCallSite site
             ThreadContext context,
             IRubyObject caller,
             IRubyObject selfthrows Throwable {
         RubyClass selfClass = pollAndGetClass(contextself);
         String method = site.name();
         SwitchPoint switchPoint = (SwitchPoint)selfClass.getInvalidator().getData();
         CacheEntry entry = selfClass.searchWithCache(method);
         
         if (methodMissing(entrysite.callType(), methodcaller)) {
             return callMethodMissing(entrysite.callType(), contextselfmethod);
         }
         
         MethodHandle target = getTarget(siteselfClassmethodentry, 0);
         target = updateInvocationTarget(targetsiteselfClassmethodentryswitchPointfalse, 0);
 
         return (IRubyObject)target.invokeWithArguments(contextcallerself);
     }
 
     public static IRubyObject invocationFallback(JRubyCallSite siteThreadContext contextIRubyObject callerIRubyObject selfIRubyObject arg0throws Throwable {
         RubyClass selfClass = pollAndGetClass(contextself);
         String method = site.name();
         SwitchPoint switchPoint = (SwitchPoint)selfClass.getInvalidator().getData();
         CacheEntry entry = selfClass.searchWithCache(method);
         if (methodMissing(entrysite.callType(), methodcaller)) {
             IRubyObject mmResult = callMethodMissing(entrysite.callType(), contextselfmethodarg0);
             // TODO: replace with handle logic
             return site.isAttrAssign() ? arg0 : mmResult;
         }
         
         MethodHandle target = getTarget(siteselfClassmethodentry, 1);
         target = updateInvocationTarget(targetsiteselfClassmethodentryswitchPointfalse, 1);
 
         return (IRubyObject)target.invokeWithArguments(contextcallerselfarg0);
     }
 
     public static IRubyObject invocationFallback(JRubyCallSite siteThreadContext contextIRubyObject callerIRubyObject selfIRubyObject arg0IRubyObject arg1throws Throwable {
         RubyClass selfClass = pollAndGetClass(contextself);
         String method = site.name();
         SwitchPoint switchPoint = (SwitchPoint)selfClass.getInvalidator().getData();
         CacheEntry entry = selfClass.searchWithCache(method);
         if (methodMissing(entrysite.callType(), methodcaller)) {
             IRubyObject mmResult = callMethodMissing(entrysite.callType(), contextselfmethodarg0arg1);
             // TODO: replace with handle logic
             return site.isAttrAssign() ? arg1 : mmResult;
         }
         
         MethodHandle target = getTarget(siteselfClassmethodentry, 2);
         target = updateInvocationTarget(targetsiteselfClassmethodentryswitchPointfalse, 2);
 
         return (IRubyObject)target.invokeWithArguments(contextcallerselfarg0arg1);
     }
 
     public static IRubyObject invocationFallback(JRubyCallSite siteThreadContext contextIRubyObject callerIRubyObject selfIRubyObject arg0IRubyObject arg1IRubyObject arg2throws Throwable {
         RubyClass selfClass = pollAndGetClass(contextself);
         String method = site.name();
         SwitchPoint switchPoint = (SwitchPoint)selfClass.getInvalidator().getData();
         CacheEntry entry = selfClass.searchWithCache(method);
         if (methodMissing(entrysite.callType(), methodcaller)) {
             IRubyObject mmResult = callMethodMissing(entrysite.callType(), contextselfmethodarg0arg1arg2);
             // TODO: replace with handle logic
             return site.isAttrAssign() ? arg2 : mmResult;
         }
         
         MethodHandle target = getTarget(siteselfClassmethodentry, 3);
         target = updateInvocationTarget(targetsiteselfClassmethodentryswitchPointfalse, 3);
 
         return (IRubyObject)target.invokeWithArguments(contextcallerselfarg0arg1arg2);
     }
     
     public static IRubyObject invocationFallback(JRubyCallSite siteThreadContext contextIRubyObject callerIRubyObject selfIRubyObject[] argsthrows Throwable {
         RubyClass selfClass = pollAndGetClass(contextself);
         String method = site.name();
         SwitchPoint switchPoint = (SwitchPoint)selfClass.getInvalidator().getData();
         CacheEntry entry = selfClass.searchWithCache(method);
         if (methodMissing(entrysite.callType(), methodcaller)) {
             IRubyObject mmResult = callMethodMissing(entrysite.callType(), contextselfmethodargs);
             // TODO: replace with handle logic
             return site.isAttrAssign() ? args[args.length - 1] : mmResult;
         }
         
         MethodHandle target = getTarget(siteselfClassmethodentry, -1);
         target = updateInvocationTarget(targetsiteselfClassmethodentryswitchPointfalse, 4);
 
         return (IRubyObject)target.invokeWithArguments(contextcallerselfargs);
     }
 
     public static IRubyObject invocationFallback(JRubyCallSite siteThreadContext contextIRubyObject callerIRubyObject selfBlock blockthrows Throwable {
         RubyClass selfClass = pollAndGetClass(contextself);
         String method = site.name();
         SwitchPoint switchPoint = (SwitchPoint)selfClass.getInvalidator().getData();
         CacheEntry entry = selfClass.searchWithCache(method);
 
         if (methodMissing(entrysite.callType(), methodcaller)) {
             try {
                 return callMethodMissing(entrysite.callType(), contextselfmethodblock);
             } catch (JumpException.BreakJump bj) {
                 return handleBreakJump(contextbj);
             } catch (JumpException.RetryJump rj) {
                 return retryJumpError(context);
             } finally {
                 if (site.isIterator()) block.escape();
             }
         }
 
         MethodHandle target = getTarget(siteselfClassmethodentry, 0);
         target = updateInvocationTarget(targetsiteselfClassmethodentryswitchPointtrue, 0);
 
         return (IRubyObjecttarget.invokeWithArguments(contextcallerselfblock);
     }
 
     public static IRubyObject invocationFallback(JRubyCallSite siteThreadContext contextIRubyObject callerIRubyObject selfIRubyObject arg0Block blockthrows Throwable {
         RubyClass selfClass = pollAndGetClass(contextself);
         String method = site.name();
         SwitchPoint switchPoint = (SwitchPoint)selfClass.getInvalidator().getData();
         CacheEntry entry = selfClass.searchWithCache(method);
 
         if (methodMissing(entrysite.callType(), methodcaller)) {
             try {
                 return callMethodMissing(entrysite.callType(), contextselfmethodarg0block);
             } catch (JumpException.BreakJump bj) {
                 return handleBreakJump(contextbj);
             } catch (JumpException.RetryJump rj) {
                 return retryJumpError(context);
             } finally {
                 if (site.isIterator()) block.escape();
             }
         }
 
         MethodHandle target = getTarget(siteselfClassmethodentry, 1);
         target = updateInvocationTarget(targetsiteselfClassmethodentryswitchPointtrue, 1);
 
         return (IRubyObjecttarget.invokeWithArguments(contextcallerselfarg0block);
     }
 
     public static IRubyObject invocationFallback(JRubyCallSite siteThreadContext contextIRubyObject callerIRubyObject selfIRubyObject arg0IRubyObject arg1Block blockthrows Throwable {
         RubyClass selfClass = pollAndGetClass(contextself);
         String method = site.name();
         SwitchPoint switchPoint = (SwitchPoint)selfClass.getInvalidator().getData();
         CacheEntry entry = selfClass.searchWithCache(method);
 
         if (methodMissing(entrysite.callType(), methodcaller)) {
             try {
                 return callMethodMissing(entrysite.callType(), contextselfmethodarg0arg1block);
             } catch (JumpException.BreakJump bj) {
                 return handleBreakJump(contextbj);
             } catch (JumpException.RetryJump rj) {
                 return retryJumpError(context);
             } finally {
                 if (site.isIterator()) block.escape();
             }
         }
 
         MethodHandle target = getTarget(siteselfClassmethodentry, 2);
         target = updateInvocationTarget(targetsiteselfClassmethodentryswitchPointtrue, 2);
 
         return (IRubyObjecttarget.invokeWithArguments(contextcallerselfarg0arg1block);
     }
 
     public static IRubyObject invocationFallback(JRubyCallSite siteThreadContext contextIRubyObject callerIRubyObject selfIRubyObject arg0IRubyObject arg1IRubyObject arg2Block blockthrows Throwable {
         RubyClass selfClass = pollAndGetClass(contextself);
         String method = site.name();
         SwitchPoint switchPoint = (SwitchPoint)selfClass.getInvalidator().getData();
         CacheEntry entry = selfClass.searchWithCache(method);
 
         if (methodMissing(entrysite.callType(), methodcaller)) {
             try {
                 return callMethodMissing(entrysite.callType(), contextselfmethodarg0arg1arg2block);
             } catch (JumpException.BreakJump bj) {
                 return handleBreakJump(contextbj);
             } catch (JumpException.RetryJump rj) {
                 return retryJumpError(context);
             } finally {
                 if (site.isIterator()) block.escape();
             }
         }
 
         MethodHandle target = getTarget(siteselfClassmethodentry, 3);
         target = updateInvocationTarget(targetsiteselfClassmethodentryswitchPointtrue, 3);
 
         return (IRubyObjecttarget.invokeWithArguments(contextcallerselfarg0arg1arg2block);
     }
 
     public static IRubyObject invocationFallback(JRubyCallSite siteThreadContext contextIRubyObject callerIRubyObject selfIRubyObject[] argsBlock blockthrows Throwable {
         RubyClass selfClass = pollAndGetClass(contextself);
         String method = site.name();
         SwitchPoint switchPoint = (SwitchPoint)selfClass.getInvalidator().getData();
         CacheEntry entry = selfClass.searchWithCache(method);
 
         if (methodMissing(entrysite.callType(), methodcaller)) {
             try {
                 return callMethodMissing(entrysite.callType(), contextselfmethodargsblock);
             } catch (JumpException.BreakJump bj) {
                 return handleBreakJump(contextbj);
             } catch (JumpException.RetryJump rj) {
                 return retryJumpError(context);
             } finally {
                 if (site.isIterator()) block.escape();
             }
         }
 
         MethodHandle target = getTarget(siteselfClassmethodentry, -1);
         target = updateInvocationTarget(targetsiteselfClassmethodentryswitchPointtrue, 4);
 
         return (IRubyObjecttarget.invokeWithArguments(contextcallerselfargsblock);
     }

    
Update the given call site using the new target, wrapping with appropriate guard and argument-juggling logic. Return a handle suitable for invoking with the site's original method type.
 
     private static MethodHandle updateInvocationTarget(MethodHandle targetJRubyCallSite siteRubyModule selfClassString nameCacheEntry entrySwitchPoint switchPointboolean blockint arity) {
         if (target == null ||
                 site.clearCount() > . ||
                 (!site.hasSeenType(selfClass.id)
                 && site.seenTypesCount() > .)) {
             site.setTarget(target = createFail((block?:)[arity], sitenameentry.method));
         } else {
             target = postProcess(sitetarget);
             
             boolean curry;
             MethodHandle fallback;
             MethodHandle gwt;
 
             // if we've cached no types, and the site is bound and we haven't seen this new type...
             if (site.seenTypesCount() > 0 && site.getTarget() != null && !site.hasSeenType(selfClass.id)) {
                 // stack it up into a PIC
                 if (..info(name + "\tadded to PIC " + logMethod(entry.method));
                 fallback = site.getTarget();
                 curry = false;
             } else {
                 // wipe out site with this new type and method
                 String bind = site.boundOnce() ? "rebind" : "bind";
                 if (..info(name + "\ttriggered site #" + site.siteID() + " " + bind + " (" + site.file() + ":" + site.line() + ")");
                 fallback = (block?:)[arity];
                 site.clearTypes();
                 curry = true;
             }
 
             site.addType(selfClass.id);
             gwt = createGWT(selfClass, (block?:)[arity], targetfallbackentrysitecurry);
             
                 // wrap in switchpoint for mutation invalidation
                 gwt = switchPoint.guardWithTest(gwtcurry ? insertArguments(fallback, 0, site) : fallback);
             }
             
             site.setTarget(gwt);
         }
         
         return target;
     }
     
     public static IRubyObject yieldSpecificFallback(
             Block block,
             ThreadContext contextthrows Throwable {
         return block.yieldSpecific(context);
     }
     
     public static IRubyObject yieldSpecificFallback(
             Block block,
             ThreadContext context,
             IRubyObject arg0throws Throwable {
         return block.yieldSpecific(contextarg0);
     }
     
     public static IRubyObject yieldSpecificFallback(
             Block block,
             ThreadContext context,
             IRubyObject arg0,
             IRubyObject arg1throws Throwable {
         return block.yieldSpecific(contextarg0arg1);
     }
     
     public static IRubyObject yieldSpecificFallback(
             Block block,
             ThreadContext context,
             IRubyObject arg0,
             IRubyObject arg1,
             IRubyObject arg2throws Throwable {
         return block.yieldSpecific(contextarg0arg1arg2);
     }
 
     private static MethodHandle createFail(MethodHandle failJRubyCallSite siteString nameDynamicMethod method) {
         if (..info(name + "\tbound to inline cache failed " + logMethod(method));
         
         MethodHandle myFail = insertArguments(fail, 0, site);
         myFail = postProcess(sitemyFail);
         return myFail;
     }
 
     private static MethodHandle createGWT(RubyModule selfClassMethodHandle testMethodHandle targetMethodHandle fallbackCacheEntry entryJRubyCallSite siteboolean curryFallback) {
         MethodHandle myTest =
                 . ?
                 insertArguments(test, 0, selfClass) :
                 insertArguments(test, 0, entry.token);
         MethodHandle myFallback = curryFallback ? insertArguments(fallback, 0, site) : fallback;
         MethodHandle guardWithTest = guardWithTest(myTesttargetmyFallback);
         
         return guardWithTest;
     }
     
     private static class IndirectBindingException extends RuntimeException {
         public IndirectBindingException(String reason) {
             super(reason);
         }
     }
 
     private static MethodHandle tryDispatchDirect(JRubyCallSite siteString nameRubyClass clsDynamicMethod method) {
         // get the "real" method in a few ways
         while (method instanceof AliasMethod) {
             name = ((AliasMethod)method).getOldName(); // need to use original name, not aliased name
             method = method.getRealMethod();
         }
         while (method instanceof WrapperMethodmethod = method.getRealMethod();
 
         if (method instanceof DefaultMethod) {
             DefaultMethod defaultMethod = (DefaultMethodmethod;
             if (defaultMethod.getMethodForCaching() instanceof JittedMethod) {
                 method = defaultMethod.getMethodForCaching();
             }
         }
 
         DynamicMethod.NativeCall nativeCall = method.getNativeCall();
 
         int siteArgCount = getSiteCount(site.type().parameterArray());
 
         if (method instanceof AttrReaderMethod) {
             // attr reader
             if (!.) {
                 throw new IndirectBindingException("direct attribute dispatch not enabled");
             }
             if (siteArgCount != 0) {
                 throw new IndirectBindingException("attr reader with > 0 args");
             }
         } else if (method instanceof AttrWriterMethod) {
             // attr writer
             if (!.) {
                 throw new IndirectBindingException("direct attribute dispatch not enabled");
             }
             if (siteArgCount != 1) {
                 throw new IndirectBindingException("attr writer with > 1 args");
             }
 
         } else if (method instanceof org.jruby.ext.ffi.jffi.DefaultMethod || method instanceof org.jruby.ext.ffi.jffi.JITNativeInvoker) {
             // if frame/scope required, can't dispatch direct
             if (method.getCallConfig() != .) {
                 throw new IndirectBindingException("frame or scope required: " + method.getCallConfig());
             }
 
             if (!method.getArity().isFixed()) {
                 throw new IndirectBindingException("fixed arity required: " + method.getArity());
             }
 
             // Arity must match, otherwise let the indirect method process errors
             if (method.getArity().getValue() != siteArgCount) {
                 throw new IndirectBindingException("arity mismatch");
             }
 
             // Only support 0..6 parameters
             if (method.getArity().getValue() > 6) {
                 throw new IndirectBindingException("target args > 6");
             }
 
             if (site.type().parameterType(site.type().parameterCount() - 1) == Block.class) {
                 // Called with a block to substitute for a callback param - cannot bind directly
                 throw new IndirectBindingException("callback block supplied");
             }
 
         } else if (nativeCall != null) {
             // has an explicit native call path
 
             if (nativeCall.isJava()) {
                 if (!.) {
                     throw new IndirectBindingException("direct Java dispatch not enabled");
                 }
 
                 // if Java, must:
                 // * match arity <= 3
                 // * not be passed a block (no coercion yet)
                 // * be a normal wrapper around a class or module (not a Ruby subclass)
                 if (nativeCall.getNativeSignature().length != siteArgCount
                         || siteArgCount > 3
                         || site.isIterator()
                         || !cls.getJavaProxy()) {
                     throw new IndirectBindingException("Java call arity mismatch or > 3 args");
                 }
             } else {
                 // if non-Java, must:
                 // * exactly match arities or both are [] boxed
                 // * 3 or fewer arguments
 
                 int nativeArgCount = (method instanceof CompiledMethod || method instanceof JittedMethod)
                         ? getRubyArgCount(nativeCall.getNativeSignature())
                         : getArgCount(nativeCall.getNativeSignature(), nativeCall.isStatic());
 
                 // arity must match or both be [] args
                 if (nativeArgCount != siteArgCount) {
                     throw new IndirectBindingException("arity mismatch or varargs at call site: " + nativeArgCount + " != " + siteArgCount);
                 }
             }
         } else {
             throw new IndirectBindingException("no direct path available for " + method.getClass().getName());
         }
 
         return handleForMethod(sitenameclsmethod);
     }
 
     private static MethodHandle getTarget(JRubyCallSite siteRubyClass clsString nameCacheEntry entryint arity) {
         IndirectBindingException ibe;
         try {
             return tryDispatchDirect(sitenameclsentry.method);
         } catch (IndirectBindingException _ibe) {
             ibe = _ibe;
             // proceed with indirect, if enabled
         }
 
         // if indirect indy-bound methods (via DynamicMethod.call) are disabled, bail out
         if (!.) {
             if (.)
                 .info(name + "\tfailed to bind to " + logMethod(entry.method) + ": " + ibe.getMessage());
             return null;
         }
 
         // no direct native path, use DynamicMethod.call
             .info(name + "\tbound indirectly to " + logMethod(entry.method) + ": " + ibe.getMessage());
 
         MethodHandle dynMethodTarget = getDynamicMethodTarget(site.type(), arityentry.method);
         dynMethodTarget = insertArguments(dynMethodTarget, 4, name);
         dynMethodTarget = insertArguments(dynMethodTarget, 0, entry);
 
         return dynMethodTarget;
     }
     
     private static MethodHandle handleForMethod(JRubyCallSite siteString nameRubyClass clsDynamicMethod method) {
         MethodHandle nativeTarget = null;
         
         if (method instanceof AttrReaderMethod) {
             // Ruby to attr reader
             if (..info(name + "\tbound as attr reader " + logMethod(method) + ":" + ((AttrReaderMethod)method).getVariableName());
             nativeTarget = createAttrReaderHandle(siteclsmethod);
         } else if (method instanceof AttrWriterMethod) {
             // Ruby to attr writer
             if (..info(name + "\tbound as attr writer " + logMethod(method) + ":" + ((AttrWriterMethod)method).getVariableName());
             nativeTarget = createAttrWriterHandle(siteclsmethod);
 
         } else if (method instanceof org.jruby.ext.ffi.jffi.JITNativeInvoker || method instanceof org.jruby.ext.ffi.jffi.DefaultMethod) {
             // Ruby to FFI
             nativeTarget = createFFIHandle(sitemethod);
 
         } else if (method.getNativeCall() != null) {
             DynamicMethod.NativeCall nativeCall = method.getNativeCall();
 
             if (nativeCall.isJava()) {
                 // Ruby to Java
                 if (..info(name + "\tbound to Java method " + logMethod(method) + ": " + nativeCall);
                 nativeTarget = createJavaHandle(sitemethod);
             } else if (method instanceof CompiledMethod || method instanceof JittedMethod) {
                 // Ruby to Ruby
                 if (..info(name + "\tbound to Ruby method " + logMethod(method) + ": " + nativeCall);
                 nativeTarget = createRubyHandle(sitemethodname);
             } else {
                 // Ruby to Core
                 if (..info(name + "\tbound to native method " + logMethod(method) + ": " + nativeCall);
                 nativeTarget = createNativeHandle(sitemethodname);
             }
         }
                         
         // add NULL_BLOCK if needed
         if (nativeTarget != null) {
             if (
                     site.type().parameterCount() > 0
                     && site.type().parameterArray()[site.type().parameterCount() - 1] != Block.class
                     && nativeTarget.type().parameterCount() > 0
                     && nativeTarget.type().parameterType(nativeTarget.type().parameterCount() - 1) == Block.class) {
                 nativeTarget = insertArguments(nativeTargetnativeTarget.type().parameterCount() - 1, .);
             } else if (
                     site.type().parameterCount() > 0
                     && site.type().parameterArray()[site.type().parameterCount() - 1] == Block.class
                     && nativeTarget.type().parameterCount() > 0
                     && nativeTarget.type().parameterType(nativeTarget.type().parameterCount() - 1) != Block.class) {
                 // drop block if not used
                 nativeTarget = dropArguments(nativeTargetnativeTarget.type().parameterCount(), Block.class);
             }
         }
         
         return nativeTarget;
     }
 
     public static boolean testGeneration(int tokenIRubyObject self) {
         return token == ((RubyBasicObject)self).getMetaClass().getGeneration();
     }
 
     public static boolean testMetaclass(RubyModule metaclassIRubyObject self) {
         return metaclass == ((RubyBasicObject)self).getMetaClass();
     }
 
     public static boolean testRealClass(int idIRubyObject self) {
         return id == ((RubyBasicObject)self).getMetaClass().getRealClass().;
     }
     
     public static IRubyObject getLast(IRubyObject[] args) {
         return args[args.length - 1];
     }
     
     private static final MethodHandle BLOCK_ESCAPE = findStatic(InvocationLinker.class"blockEscape"methodType(void.classBlock.class));
     public static void blockEscape(Block block) {
         block.escape();
     }
     
     private static final MethodHandle HANDLE_BREAK_JUMP = findStatic(InvokeDynamicSupport.class"handleBreakJump"methodType(IRubyObject.classJumpException.BreakJump.classThreadContext.class));
 //    
 //    private static IRubyObject handleRetryJump(JumpException.RetryJump bj, ThreadContext context) {
 //        block.escape();
 //        throw context.getRuntime().newLocalJumpError(RubyLocalJumpError.Reason.RETRY, context.getRuntime().getNil(), "retry outside of rescue not supported");
 //    }
 //    private static final MethodHandle HANDLE_RETRY_JUMP = findStatic(InvokeDynamicSupport.class, "handleRetryJump", methodType(IRubyObject.class, JumpException.BreakJump.class, ThreadContext.class));
     
     private static MethodHandle postProcess(JRubyCallSite siteMethodHandle target) {
         if (site.isIterator()) {
             // wrap with iter logic for break, retry, and block escape
             MethodHandle breakHandler = permuteArguments(
                     ,
                     site.type().insertParameterTypes(0, JumpException.BreakJump.class),
                     new int[] {0, 1});
 
             target = catchException(targetJumpException.BreakJump.classbreakHandler);
 
             target = Binder
                     .from(target.type())
                     .tryFinally(permuteArguments(site.type().changeReturnType(void.class), site.type().parameterCount() - 1))
                     .invoke(target);
         }
         
         // if it's an attr assignment as an expression, need to return n-1th argument
         if (site.isAttrAssign() && site.isExpression()) {
             // return given argument
             MethodHandle newTarget = identity(IRubyObject.class);
             
             // if args are IRubyObject[].class, yank out n-1th
             if (site.type().parameterArray()[site.type().parameterCount() - 1] == IRubyObject[].class) {
                 newTarget = filterArguments(newTarget, 0, findStatic(InvocationLinker.class"getLast"methodType(IRubyObject.classIRubyObject[].class))); 
             }
             
             // drop standard preamble args plus extra args
             newTarget = dropArguments(newTarget, 0, IRubyObject.classThreadContext.classIRubyObject.classIRubyObject.class);
             
             // drop extra arguments, if any
             MethodType dropped = target.type().dropParameterTypes(0, 3);
             if (dropped.parameterCount() > 1) {
                 Class[] drops = new Class[dropped.parameterCount() - 1];
                 Arrays.fill(dropsIRubyObject.class);
                 newTarget = dropArguments(newTarget, 4, drops);
             }
             
             // fold using target
             target = foldArguments(newTargettarget);
         }
         
         return target;
     }
     
     ////////////////////////////////////////////////////////////////////////////
     // Inline-caching failure paths, for megamorphic call sites
     ////////////////////////////////////////////////////////////////////////////
 
     public static IRubyObject fail(JRubyCallSite site
             ThreadContext context,
             IRubyObject caller,
             IRubyObject selfthrows Throwable {
         RubyClass selfClass = pollAndGetClass(contextself);
         String name = site.name();
         CacheEntry entry = site.entry;
         
         if (entry.typeOk(selfClass)) {
             return entry.method.call(contextselfselfClassname);
         } else {
             entry = selfClass.searchWithCache(name);
             if (methodMissing(entrysite.callType(), namecaller)) {
                 return callMethodMissing(entrysite.callType(), contextselfname);
             }
             site.entry = entry;
             return entry.method.call(contextselfselfClassname);
         }
     }
 
     public static IRubyObject fail(JRubyCallSite siteThreadContext contextIRubyObject callerIRubyObject selfIRubyObject arg0throws Throwable {
         RubyClass selfClass = pollAndGetClass(contextself);
         String name = site.name();
         CacheEntry entry = site.entry;
         
         if (entry.typeOk(selfClass)) {
             return entry.method.call(contextselfselfClassnamearg0);
         } else {
             entry = selfClass.searchWithCache(name);
             if (methodMissing(entrysite.callType(), namecaller)) {
                 return callMethodMissing(entrysite.callType(), contextselfnamearg0);
             }
             site.entry = entry;
             return entry.method.call(contextselfselfClassnamearg0);
         }
     }
 
     public static IRubyObject fail(JRubyCallSite siteThreadContext contextIRubyObject callerIRubyObject selfIRubyObject arg0IRubyObject arg1throws Throwable {
         RubyClass selfClass = pollAndGetClass(contextself);
         String name = site.name();
         CacheEntry entry = site.entry;
         
         if (entry.typeOk(selfClass)) {
             return entry.method.call(contextselfselfClassnamearg0arg1);
         } else {
             entry = selfClass.searchWithCache(name);
             if (methodMissing(entrysite.callType(), namecaller)) {
                 return callMethodMissing(entrysite.callType(), contextselfnamearg0arg1);
             }
             site.entry = entry;
             return entry.method.call(contextselfselfClassnamearg0arg1);
         }
     }
 
     public static IRubyObject fail(JRubyCallSite siteThreadContext contextIRubyObject callerIRubyObject selfIRubyObject arg0IRubyObject arg1IRubyObject arg2throws Throwable {
         RubyClass selfClass = pollAndGetClass(contextself);
         String name = site.name();
         CacheEntry entry = site.entry;
         
         if (entry.typeOk(selfClass)) {
             return entry.method.call(contextselfselfClassnamearg0arg1arg2);
         } else {
             entry = selfClass.searchWithCache(name);
             if (methodMissing(entrysite.callType(), namecaller)) {
                 return callMethodMissing(entrysite.callType(), contextselfnamearg0arg1arg2);
             }
             site.entry = entry;
             return entry.method.call(contextselfselfClassnamearg0arg1arg2);
         }
     }
 
     public static IRubyObject fail(JRubyCallSite siteThreadContext contextIRubyObject callerIRubyObject selfIRubyObject[] argsthrows Throwable {
         RubyClass selfClass = pollAndGetClass(contextself);
         String name = site.name();
         CacheEntry entry = site.entry;
         
         if (entry.typeOk(selfClass)) {
             return entry.method.call(contextselfselfClassnameargs);
         } else {
             entry = selfClass.searchWithCache(name);
             if (methodMissing(entrysite.callType(), namecaller)) {
                 return callMethodMissing(entrysite.callType(), contextselfnameargs);
             }
             site.entry = entry;
             return entry.method.call(contextselfselfClassnameargs);
         }
     }
 
     public static IRubyObject fail(JRubyCallSite siteThreadContext contextIRubyObject callerIRubyObject selfBlock blockthrows Throwable {
         RubyClass selfClass = pollAndGetClass(contextself);
         String name = site.name();
         CacheEntry entry = site.entry;
         
         if (entry.typeOk(selfClass)) {
             return entry.method.call(contextselfselfClassnameblock);
         } else {
             entry = selfClass.searchWithCache(name);
             if (methodMissing(entrysite.callType(), namecaller)) {
                 return callMethodMissing(entrysite.callType(), contextselfnameblock);
             }
             site.entry = entry;
             return entry.method.call(contextselfselfClassnameblock);
         }
     }
 
     public static IRubyObject fail(JRubyCallSite siteThreadContext contextIRubyObject callerIRubyObject selfIRubyObject arg0Block blockthrows Throwable {
         RubyClass selfClass = pollAndGetClass(contextself);
         String name = site.name();
         CacheEntry entry = site.entry;
         
         if (entry.typeOk(selfClass)) {
             return entry.method.call(contextselfselfClassnamearg0block);
         } else {
             entry = selfClass.searchWithCache(name);
             if (methodMissing(entrysite.callType(), namecaller)) {
                 return callMethodMissing(entrysite.callType(), contextselfnamearg0block);
             }
             site.entry = entry;
             return entry.method.call(contextselfselfClassnamearg0block);
         }
     }
 
     public static IRubyObject fail(JRubyCallSite siteThreadContext contextIRubyObject callerIRubyObject selfIRubyObject arg0IRubyObject arg1Block blockthrows Throwable {
         RubyClass selfClass = pollAndGetClass(contextself);
         String name = site.name();
         CacheEntry entry = site.entry;
         
         if (entry.typeOk(selfClass)) {
             return entry.method.call(contextselfselfClassnamearg0arg1block);
         } else {
             entry = selfClass.searchWithCache(name);
             if (methodMissing(entrysite.callType(), namecaller)) {
                 return callMethodMissing(entrysite.callType(), contextselfnamearg0arg1block);
             }
             site.entry = entry;
             return entry.method.call(contextselfselfClassnamearg0arg1block);
         }
     }
 
     public static IRubyObject fail(JRubyCallSite siteThreadContext contextIRubyObject callerIRubyObject selfIRubyObject arg0IRubyObject arg1IRubyObject arg2Block blockthrows Throwable {
         RubyClass selfClass = pollAndGetClass(contextself);
         String name = site.name();