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) 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 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.runtime.invokedynamic;
  
  
  import static java.lang.invoke.MethodHandles.*;
  
  import java.util.Arrays;
  
  import org.jruby.*;
  
  import static java.lang.invoke.MethodType.methodType;
  import java.util.List;
  import static org.jruby.runtime.invokedynamic.InvokeDynamicSupport.*;
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(siteselfClassentry, 0);
         target = updateInvocationTarget(targetsiteselfselfClassmethodentryswitchPointfalse, 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(siteselfClassentry, 1);
         target = updateInvocationTarget(targetsiteselfselfClassmethodentryswitchPointfalse, 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(siteselfClassentry, 2);
         target = updateInvocationTarget(targetsiteselfselfClassmethodentryswitchPointfalse, 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(siteselfClassentry, 3);
         target = updateInvocationTarget(targetsiteselfselfClassmethodentryswitchPointfalse, 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(siteselfClassentry, -1);
         target = updateInvocationTarget(targetsiteselfselfClassmethodentryswitchPointfalse, 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(siteselfClassentry, 0);
         target = updateInvocationTarget(targetsiteselfselfClassmethodentryswitchPointtrue, 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(siteselfClassentry, 1);
         target = updateInvocationTarget(targetsiteselfselfClassmethodentryswitchPointtrue, 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(siteselfClassentry, 2);
         target = updateInvocationTarget(targetsiteselfselfClassmethodentryswitchPointtrue, 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(siteselfClassentry, 3);
         target = updateInvocationTarget(targetsiteselfselfClassmethodentryswitchPointtrue, 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(siteselfClassentry, -1);
         target = updateInvocationTarget(targetsiteselfselfClassmethodentryswitchPointtrue, 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 siteIRubyObject selfRubyModule selfClassString nameCacheEntry entrySwitchPoint switchPointboolean blockint arity) {
         if (target == null ||
                 site.clearCount() > ..load() ||
                 (!site.hasSeenType(selfClass.id)
                 && site.seenTypesCount() + 1 > ..load())) {
             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 (..load()) .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 (..load()) .info(name + "\ttriggered site #" + site.siteID() + " " + bind + " (" + site.file() + ":" + site.line() + ")");
                 fallback = (block?:)[arity];
                 site.clearTypes();
                 curry = true;
             }
 
             site.addType(selfClass.id);
             
             SmartHandle test;
             SmartBinder selfTest = SmartBinder
                                           .from(site.signature().asFold(boolean.class))
                                           .permute("self");
             
             if (self instanceof RubySymbol ||
                     self instanceof RubyFixnum ||
                     self instanceof RubyFloat ||
                     self instanceof RubyNil ||
                     self instanceof RubyBoolean.True ||
                     self instanceof RubyBoolean.False) {
                 
                 test = selfTest
                         .insert(1, "selfJavaType"self.getClass())
                         .cast(boolean.classObject.classClass.class)
                         .invoke();
                 
             } else {
                 
                 if (..load()) {
                     selfTest = selfTest.insert(0, "selfClass"selfClass);
                 } else {
                     selfTest = selfTest.insert(0, "token"entry.token);
                 }
                 
                 test = selfTest
                         .cast(boolean.classRubyClass.classIRubyObject.class)
                         .invoke();
             }
             
             gwt = createGWT(testtargetfallbackentrysitecurry);
             
             if (..load()) {
                 // 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 (..load()) .info(name + "\tbound to inline cache failed " + logMethod(method));
         
         MethodHandle myFail = insertArguments(fail, 0, site);
         myFail = postProcess(sitemyFail);
         return myFail;
     }
 
     private static MethodHandle createGWT(SmartHandle testMethodHandle targetMethodHandle fallbackCacheEntry entryJRubyCallSite siteboolean curryFallback) {
         MethodHandle myFallback = curryFallback ? insertArguments(fallback, 0, site) : fallback;
         MethodHandle guardWithTest = test.guard(targetmyFallback);
         
         return guardWithTest;
     }
 
     private static int getNativeArgCount(DynamicMethod methodNativeCall nativeCall) {
         // 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());
         return nativeArgCount;
     }
 
     public static DynamicMethod unwrapMethod(DynamicMethod methodString[] realNamethrows IndirectBindingException {
         // get the "real" method in a few ways
         while (method instanceof AliasMethod) {
             realName[0] = ((AliasMethod)method).getOldName(); // need to use original name, not aliased name
             method = method.getRealMethod();
         }
         while (method instanceof WrapperMethodmethod = method.getRealMethod();
         // ProfilingDynamicMethod wraps any number of other types of methods but
         // we do not handle it in indy binding right now. Disable direct binding
         // and bind through DynamicMethod.
         if (method instanceof ProfilingDynamicMethod) {
             throw new IndirectBindingException("profiling active");
         }
         if (method instanceof DefaultMethod) {
             DefaultMethod defaultMethod = (DefaultMethodmethod;
             if (defaultMethod.getMethodForCaching() instanceof JittedMethod) {
                 method = defaultMethod.getMethodForCaching();
             }
         }
         return method;
     }
     
     private static class IndirectBindingException extends RuntimeException {
         public IndirectBindingException(String reason) {
             super(reason);
         }
     }
     
     public interface HandleGenerator {
         public boolean canGenerate(JRubyCallSite siteRubyClass clsDynamicMethod method);
         public MethodHandle generate(JRubyCallSite siteRubyClass clsDynamicMethod methodString realName);
     }
     
     public static class HandleMethodGenerator implements HandleGenerator {
         
         @Override
         public boolean canGenerate(JRubyCallSite siteRubyClass clsDynamicMethod method) {
             return method instanceof HandleMethod;
         }
         
         @Override
         public MethodHandle generate(JRubyCallSite siteRubyClass clsDynamicMethod methodString realName) {
             MethodHandle handle = ((HandleMethod)method).getHandle(site.arity());
             
             if (handle == null) {
                 throw new IndirectBindingException("MH dynamic method does not have needed arity");
             }
             
             if (..load()) .info(site.name() + "\tbound from MHDynMethod " + logMethod(method) + ":" + handle);
             
             Signature fullSig = site.fullSignature();
             MethodHandle nativeTarget =  Binder
                     .from(fullSig.type())
                     .permute(fullSig.to("context""self""arg*""block"))
                     .invoke(handle);
             nativeTarget = addOrRemoveBlock(sitenativeTarget);
             
             return nativeTarget;            
         }
     }
     
     public static class AttrReaderGenerator implements HandleGenerator {
 
         @Override
         public boolean canGenerate(JRubyCallSite siteRubyClass clsDynamicMethod method) {
             if (method instanceof AttrReaderMethod) {
                 // attr reader
                 if (!..load()) {
                     throw new IndirectBindingException("direct attribute dispatch not enabled");
                 }
                 if (site.arity() != 0) {
                     throw new IndirectBindingException("attr reader with > 0 args");
                 }
                 
                 return true;
             }
             
             return false;
         }
 
         @Override
         public MethodHandle generate(JRubyCallSite siteRubyClass clsDynamicMethod methodString realName) {
             AttrReaderMethod attrReader = (AttrReaderMethod)method;
             String varName = attrReader.getVariableName();
 
             // we getVariableAccessorForWrite here so it is eagerly created and we don't cache the DUMMY
             VariableAccessor accessor = cls.getRealClass().getVariableAccessorForWrite(varName);
             
             // Ruby to attr reader
             if (..load()) {
                 if (accessor instanceof FieldVariableAccessor) {
                     .info(site.name() + "\tbound as field attr reader " + logMethod(method) + ":" + ((AttrReaderMethod)method).getVariableName());
                 } else {
                     .info(site.name() + "\tbound as attr reader " + logMethod(method) + ":" + ((AttrReaderMethod)method).getVariableName());
                 }
             }
             
             return createAttrReaderHandle(siteclsaccessor);
         }
     }
 
     public static class AttrWriterGenerator implements HandleGenerator {
 
         @Override
         public boolean canGenerate(JRubyCallSite siteRubyClass clsDynamicMethod method) {
             if (method instanceof AttrWriterMethod) {
                 // attr writer
                 if (!..load()) {
                     throw new IndirectBindingException("direct attribute dispatch not enabled");
                 }
                 if (site.arity() != 1) {
                     throw new IndirectBindingException("attr writer with > 1 args");
                 }
                 
                 return true;
             }
             
             return false;
         }
 
         @Override
         public MethodHandle generate(JRubyCallSite siteRubyClass clsDynamicMethod methodString realName) {
             AttrWriterMethod attrReader = (AttrWriterMethod)method;
             String varName = attrReader.getVariableName();
 
             // we getVariableAccessorForWrite here so it is eagerly created and we don't cache the DUMMY
             VariableAccessor accessor = cls.getRealClass().getVariableAccessorForWrite(varName);
             
             // Ruby to attr reader
             if (..load()) {
                 if (accessor instanceof FieldVariableAccessor) {
                     .info(site.name() + "\tbound as field attr writer " + logMethod(method) + ":" + ((AttrWriterMethodmethod).getVariableName());
                 } else {
                     .info(site.name() + "\tbound as attr writer " + logMethod(method) + ":" + ((AttrWriterMethodmethod).getVariableName());
                 }
             }
             return createAttrWriterHandle(siteclsaccessor);
         }
     }
     
     public static class FFIGenerator implements HandleGenerator {
 
         @Override
         public boolean canGenerate(JRubyCallSite siteRubyClass clsDynamicMethod method) {
             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() != site.arity()) {
                     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");
                 }
                 
                 return true;
             }
             
             return false;
         }
 
         @Override
         public MethodHandle generate(JRubyCallSite siteRubyClass clsDynamicMethod methodString realName) {
             // Ruby to FFI
             return createFFIHandle(sitemethod);
         }
         
     }
     
     public static class JavaCallGenerator implements HandleGenerator {
 
         @Override
         public boolean canGenerate(JRubyCallSite siteRubyClass clsDynamicMethod method) {
             NativeCall nativeCall = method.getNativeCall();
             
             if (nativeCall != null) {
                 // has an explicit native call path
 
                 if (nativeCall.isJava()) {
                     if (!..load()) {
                         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 != site.arity()
                             || site.arity() > 3
                             || site.isIterator()
                             || !cls.getJavaProxy()) {
                         throw new IndirectBindingException("Java call arity mismatch or > 3 args");
                     }
                     
                     return true;
                 }
             }
             
             return false;
         }
 
         @Override
         public MethodHandle generate(JRubyCallSite siteRubyClass clsDynamicMethod methodString realName) {
             // Ruby to Java
             if (..load()) .info(site.name() + "\tbound to Java method " + logMethod(method) + ": " + method.getNativeCall());
             
             return postProcessNativeHandle(createJavaHandle(sitemethod), sitemethodfalse..load());
         }
         
     }
     
     public static class RubyCallGenerator implements HandleGenerator {
 
         @Override
         public boolean canGenerate(JRubyCallSite siteRubyClass clsDynamicMethod method) {
             NativeCall nativeCall = method.getNativeCall();
             
             if (method instanceof CompiledMethod || method instanceof JittedMethod) {
                 if (nativeCall != null) {
                     int nativeArgCount = getNativeArgCount(methodmethod.getNativeCall());
                     
                     // arity must match or both be [] args
                     if (nativeArgCount != site.arity()) {
                         throw new IndirectBindingException("arity mismatch or varargs at call site: " + nativeArgCount + " != " + site.arity());
                     }
 
                     return true;
                 }
             }
             
             return false;
         }
 
         @Override
         public MethodHandle generate(JRubyCallSite siteRubyClass clsDynamicMethod methodString realName) {
             // Ruby to Ruby
             if (..load()) .info(site.name() + "\tbound to Ruby method " + logMethod(method) + ": " + method.getNativeCall());
             return postProcessNativeHandle(createRubyHandle(sitemethodrealName), sitemethodtruefalse);
         }
         
     }
     
     public static class CoreCallGenerator implements HandleGenerator {
 
         @Override
         public boolean canGenerate(JRubyCallSite siteRubyClass clsDynamicMethod method) {
             NativeCall nativeCall = method.getNativeCall();
             
             if (nativeCall != null) {
                 int nativeArgCount = getNativeArgCount(methodmethod.getNativeCall());
 
                 // arity must match or both be [] args
                 if (nativeArgCount != site.arity()) {
                     throw new IndirectBindingException("arity mismatch or varargs at call site: " + nativeArgCount + " != " + site.arity());
                 }
 
                 return true;
             }
             
             return false;
         }
 
         @Override
         public MethodHandle generate(JRubyCallSite siteRubyClass clsDynamicMethod methodString realName) {
             // Ruby to Core
             if (..load()) .info(site.name() + "\tbound to native method " + logMethod(method) + ": " + method.getNativeCall());
             return postProcessNativeHandle(createNativeHandle(cls.getClassRuntime(), sitemethodrealName), sitemethodtruefalse);
         }
         
     }
     
     private static final List<HandleGeneratorHANDLE_GENERATORS =  Arrays.<HandleGenerator>asList(
             new HandleMethodGenerator(),
             new AttrReaderGenerator(),
             new AttrWriterGenerator(),
             new FFIGenerator(),
             new JavaCallGenerator(),
             new RubyCallGenerator(),
             new CoreCallGenerator()
             );
 
     private static MethodHandle tryDispatchDirect(JRubyCallSite siteRubyClass clsDynamicMethod method) {
         String[] realName = {site.name()};
         method = unwrapMethod(methodrealName);
         
         for (HandleGenerator generator : ) {
             if (generator.canGenerate(siteclsmethod)) {
                 return generator.generate(siteclsmethodrealName[0]);
             }
         }
         
         throw new IndirectBindingException("no direct path available for " + method.getClass().getName());
     }
 
     private static MethodHandle getTarget(JRubyCallSite siteRubyClass clsCacheEntry entryint arity) {
         IndirectBindingException ibe;
         try {
             return tryDispatchDirect(siteclsentry.method);
         } catch (IndirectBindingException _ibe) {
             ibe = _ibe;
             // proceed with indirect, if enabled
         }
 
         // if indirect indy-bound methods (via DynamicMethod.call) are disabled, bail out
         if (!..load()) {
             if (..load())
                 .info(site.name() + "\tfailed to bind to " + logMethod(entry.method) + ": " + ibe.getMessage());
             return null;
         }
 
         // no direct native path, use DynamicMethod.call
         if (..load())
             .info(site.name() + "\tbound indirectly to " + logMethod(entry.method) + ": " + ibe.getMessage());
 
         MethodHandle dynMethodTarget = getDynamicMethodTarget(site.type(), arityentry.method);
         dynMethodTarget = insertArguments(dynMethodTarget, 4, site.name());
         dynMethodTarget = insertArguments(dynMethodTarget, 0, entry.method);
 
         return dynMethodTarget;
     }
     
     private static MethodHandle postProcessNativeHandle(MethodHandle nativeTargetJRubyCallSite siteDynamicMethod methodboolean checkArityboolean rewriteStackTrace) {
         if (nativeTarget != null) {
             nativeTarget = addOrRemoveBlock(sitenativeTarget);
             
             // add arity check if needed
             if (checkArity) {
                 int nativeArgCount = getNativeArgCount(methodmethod.getNativeCall());
                 
                 if (nativeArgCount == 4 && site.arity() == 4) {
                     // Arity does not give us enough information about min/max
                     // so we must go to the annotation
                     Method reflected = method.getNativeCall().getMethod();
                     JRubyMethod annotation = reflected.getAnnotation(JRubyMethod.class);
                     
                     int required = annotation.required();
                     int optional = annotation.optional();
                     boolean rest = annotation.rest();
                     
                     if (required > 0 || !rest) {
                         MethodHandle arityCheck = Binder
                                 .from(site.type().changeReturnType(void.class))
                                 .insert(0, new Class[]{int.classint.classboolean.class}, requiredoptionalrest)
                                 .invokeStaticQuiet(site.lookup(), InvocationLinker.class"checkArity");
                         nativeTarget = foldArguments(nativeTargetarityCheck);
                     }
                 }
             }
 
             if (rewriteStackTrace) {
                 SmartHandle rewriteHandle = SmartBinder.from(lookup(), site.signature().insertArg(0, "throwable"Throwable.class))
                         .permute("throwable")
                         .append("runtime"method.getImplementationClass().getRuntime())
                         .invokeStaticQuiet(lookup(), Helpers.class"rewriteStackTraceAndThrow");
 
                 nativeTarget = catchException(nativeTargetThrowable.classrewriteHandle.handle());
             }
         }
         
         return nativeTarget;
     }
 
     private static MethodHandle addOrRemoveBlock(JRubyCallSite siteMethodHandle nativeTarget) {
         // add NULL_BLOCK if needed
         if (
                 site.type().parameterCount() > 0
                 && site.type().parameterArray()[site.type().parameterCount() - 1] != Block.class
                 && nativeTarget.type().parameterCount() > 0
                 && nativeTarget.type().parameterType(nativeTarget.