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) 2006 The JRuby Community <www.jruby.org> Copyright (C) 2006 Ola Bini <ola@ologix.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 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.internal.runtime.methods;
  
  import java.util.Arrays;
  import java.util.List;
  import org.jruby.Ruby;
  import  org.objectweb.asm.ClassWriter;
  import  org.objectweb.asm.Opcodes;
  import static org.jruby.util.CodegenUtils.*;
  import static java.lang.System.*;
  import  org.objectweb.asm.ClassReader;
  import  org.objectweb.asm.Label;
  import  org.objectweb.asm.util.CheckClassAdapter;
In order to avoid the overhead with reflection-based method handles, this MethodFactory uses ASM to generate tiny invoker classes. This allows for better performance and more specialization per-handle than can be supported via reflection. It also allows optimizing away many conditionals that can be determined once ahead of time. When running in secured environments, this factory may not function. When this can be detected, MethodFactory will fall back on the reflection-based factory instead.

See also:
org.jruby.runtime.MethodFactory
  
  public class InvocationMethodFactory extends MethodFactory implements Opcodes {
  
      private static final Logger LOG = LoggerFactory.getLogger("InvocationMethodFactory");
  
      private static final boolean DEBUG = false;
    
    
The pathname of the super class for compiled Ruby method handles.
   
      private final static String COMPILED_SUPER_CLASS = p(CompiledMethod.class);
    
    
The outward call signature for compiled Ruby method handles.
  
      private final static String COMPILED_CALL_SIG = sig(IRubyObject.class,
              params(ThreadContext.classIRubyObject.classRubyModule.classString.classIRubyObject[].class));
    
    
The outward call signature for compiled Ruby method handles.
  
      private final static String COMPILED_CALL_SIG_BLOCK = sig(IRubyObject.class,
              params(ThreadContext.classIRubyObject.classRubyModule.classString.classIRubyObject[].classBlock.class));
    
    
The outward arity-zero call-with-block signature for compiled Ruby method handles.
  
     private final static String COMPILED_CALL_SIG_ZERO_BLOCK = sig(IRubyObject.class,
             params(ThreadContext.classIRubyObject.classRubyModule.classString.classBlock.class));
    
    
The outward arity-zero call-with-block signature for compiled Ruby method handles.
 
     private final static String COMPILED_CALL_SIG_ZERO = sig(IRubyObject.class,
             params(ThreadContext.classIRubyObject.classRubyModule.classString.class));
    
    
The outward arity-zero call-with-block signature for compiled Ruby method handles.
 
     private final static String COMPILED_CALL_SIG_ONE_BLOCK = sig(IRubyObject.class,
             params(ThreadContext.classIRubyObject.classRubyModule.classString.classIRubyObject.classBlock.class));
    
    
The outward arity-zero call-with-block signature for compiled Ruby method handles.
 
     private final static String COMPILED_CALL_SIG_ONE = sig(IRubyObject.class,
             params(ThreadContext.classIRubyObject.classRubyModule.classString.classIRubyObject.class));
    
    
The outward arity-zero call-with-block signature for compiled Ruby method handles.
 
     private final static String COMPILED_CALL_SIG_TWO_BLOCK = sig(IRubyObject.class,
             params(ThreadContext.classIRubyObject.classRubyModule.classString.classIRubyObject.classIRubyObject.classBlock.class));
    
    
The outward arity-zero call-with-block signature for compiled Ruby method handles.
 
     private final static String COMPILED_CALL_SIG_TWO = sig(IRubyObject.class,
             params(ThreadContext.classIRubyObject.classRubyModule.classString.classIRubyObject.classIRubyObject.class));
    
    
The outward arity-zero call-with-block signature for compiled Ruby method handles.
 
     private final static String COMPILED_CALL_SIG_THREE_BLOCK = sig(IRubyObject.class,
             params(ThreadContext.classIRubyObject.classRubyModule.classString.classIRubyObject.classIRubyObject.classIRubyObject.classBlock.class));
    
    
The outward arity-zero call-with-block signature for compiled Ruby method handles.
 
     private final static String COMPILED_CALL_SIG_THREE = sig(IRubyObject.class,
             params(ThreadContext.classIRubyObject.classRubyModule.classString.classIRubyObject.classIRubyObject.classIRubyObject.class));
 
     private final static String BLOCK_CALL_SIG = sig(.params(
             ThreadContext.class.IRubyObject.classBlock.class));
     private final static String BLOCK_CALL_SIG19 = sig(.params(
             ThreadContext.classIRubyObject.classIRubyObject[].classBlock.class));
    
    
The super constructor signature for Java-based method handles.
 
     private final static String JAVA_SUPER_SIG = sig(.params(RubyModule.classVisibility.class));
    
    
The lvar index of "this"
 
     public static final int THIS_INDEX = 0;
    
    
The lvar index of the passed-in ThreadContext
 
     public static final int THREADCONTEXT_INDEX = 1;
    
    
The lvar index of the method-receiving object
 
     public static final int RECEIVER_INDEX = 2;
    
    
The lvar index of the RubyClass being invoked against
 
     public static final int CLASS_INDEX = 3;
    
    
The lvar index method name being invoked
 
     public static final int NAME_INDEX = 4;
    
    
The lvar index of the method args on the call
 
     public static final int ARGS_INDEX = 5;
    
    
The lvar index of the passed-in Block on the call
 
     public static final int BLOCK_INDEX = 6;

    
The classloader to use for code loading
 
     protected final JRubyClassLoader classLoader;
    
    
An object to sync against when loading classes, to avoid dups
 
     protected final Object syncObject;
    
    
Whether this factory has seen undefined methods already. This is used to detect likely method handle collisions when we expect to create a new handle for each call.
 
     private boolean seenUndefinedClasses = false;

    
Whether we've informed the user that we've seen undefined methods; this is to avoid a flood of repetitive information.
 
     private boolean haveWarnedUser = false;
    
    
Construct a new InvocationMethodFactory using the specified classloader to load code. If the target classloader is not an instance of JRubyClassLoader, it will be wrapped with one.

Parameters:
classLoader The classloader to use, or to wrap if it is not a JRubyClassLoader instance.
 
     public InvocationMethodFactory(ClassLoader classLoader) {
         // use the given classloader as our sync, regardless of whether we wrap it
         this. = classLoader;
         
         if (classLoader instanceof JRubyClassLoader) {
             this. = (JRubyClassLoader)classLoader;
         } else {
             this. = new JRubyClassLoader(classLoader);
         }
     }

    
Use code generation to provide a method handle for a compiled Ruby method.

See also:
org.jruby.runtime.MethodFactory.getCompiledMethod
 
             RubyModule implementationClass,
             String method,
             Arity arity,
             Visibility visibility,
             StaticScope scope,
             Object scriptObject,
             CallConfiguration callConfig,
             ISourcePosition position,
             String parameterDesc) {
 
         return new CompiledMethod.LazyCompiledMethod(
                 implementationClass,
                 method,
                 arity,
                 visibility,
                 scope,
                 scriptObject,
                 callConfig,
                 position,
                 parameterDesc,
                 new InvocationMethodFactory());
     }
 
     public static String getCompiledCallbackName(String typePathString method) {
         return (typePath + "$" + method).replaceAll("/""\\$");
     }

    
Use code generation to provide a method handle for a compiled Ruby method.

See also:
org.jruby.runtime.MethodFactory.getCompiledMethod
 
             RubyModule implementationClass,
             String method,
             Arity arity,
             Visibility visibility,
             StaticScope scope,
             Object scriptObject,
             CallConfiguration callConfig,
             ISourcePosition position,
             String parameterDesc) {
         
         Class scriptClass = scriptObject.getClass();
         String typePath = p(scriptClass);
         String invokerPath = getCompiledCallbackName(typePathmethod);
         try {
             Class generatedClass = tryClass(invokerPathscriptClass);
             if (generatedClass == null) {
                 synchronized () {
                     // try again in case someone else loaded it under us
                     generatedClass = tryClass(invokerPathscriptClass);
                     if (generatedClass == null) {
                         if (.) {
                             .debug("no generated handle in classloader for: {}"invokerPath);
                         }
                         byte[] invokerBytes = getCompiledMethodOffline(
                                 method,
                                 typePath,
                                 invokerPath,
                                 arity,
                                 scope,
                                 callConfig,
                                 position.getFile(),
                                 position.getStartLine());
                         generatedClass = endCallWithBytes(invokerBytesinvokerPath);
                     }
                 }
             } else if (.) {
                 .debug("found generated handle in classloader: {}"invokerPath);
             }
 
             CompiledMethod compiledMethod = (CompiledMethod)generatedClass.newInstance();
             compiledMethod.init(implementationClassarityvisibilityscopescriptObjectcallConfigpositionparameterDesc);
 
             Class[] params;
             if (arity.isFixed() && scope.getRequiredArgs() < 4) {
                 params = StandardASMCompiler.getStaticMethodParams(scriptClassscope.getRequiredArgs());
             } else {
                 params = StandardASMCompiler.getStaticMethodParams(scriptClass, 4);
             }
             compiledMethod.setNativeCall(scriptClassmethodIRubyObject.classparamstrue);
 
             return compiledMethod;
         } catch(Exception e) {
             e.printStackTrace();
             throw implementationClass.getRuntime().newLoadError(e.getMessage());
         }
     }

    
Use code generation to provide a method handle for a compiled Ruby method.

See also:
org.jruby.runtime.MethodFactory.getCompiledMethod
 
     @Override
     public byte[] getCompiledMethodOffline(
             String methodString classNameString invokerPathArity arity,
             StaticScope scopeCallConfiguration callConfigString filenameint line) {
         String sup = ;
         ClassWriter cw;
         cw = createCompiledCtor(invokerPathinvokerPathsup);
         SkinnyMethodAdapter mv = null;
         String signature = null;
         boolean specificArity = false;
 
         // if trace, need to at least populate a backtrace frame
         if (.) {
             switch (callConfig) {
             case :
                 callConfig = .;
                 break;
             case :
                 callConfig = .;
                 break;
             case :
                 callConfig = .;
                 break;
             }
         }
 
         if (scope.getRestArg() >= 0 || scope.getOptionalArgs() > 0 || scope.getRequiredArgs() > 3) {
             signature = ;
             mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "call"signaturenullnull);
         } else {
             specificArity = true;
 
             mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "call"nullnull);
             mv.start();
 
             // check arity
             mv.aloadMany(0, 1, 4, 5); // method, context, name, args, required
             mv.pushInt(scope.getRequiredArgs());
             mv.invokestatic(p(JavaMethod.class), "checkArgumentCount"sig(void.classJavaMethod.classThreadContext.classString.classIRubyObject[].classint.class));
 
             mv.aloadMany(0, 1, 2, 3, 4);
             for (int i = 0; i < scope.getRequiredArgs(); i++) {
                 mv.aload(5);
                 mv.ldc(i);
                 mv.arrayload();
             }
             mv.aload(6);
 
             switch (scope.getRequiredArgs()) {
             case 0:
                 signature = ;
                 break;
             case 1:
                 signature = ;
                 break;
             case 2:
                 signature = ;
                 break;
             case 3:
                 signature = ;
                 break;
             }
 
             mv.invokevirtual(invokerPath"call"signature);
             mv.areturn();
             mv.end();
 
             // Define a second version that doesn't take a block, so we have unique code paths for both cases.
             switch (scope.getRequiredArgs()) {
             case 0:
                 signature = ;
                 break;
             case 1:
                 signature = ;
                 break;
             case 2:
                 signature = ;
                 break;
             case 3:
                 signature = ;
                 break;
             }
             mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "call"signaturenullnull);
             mv.start();
 
             mv.aloadMany(0, 1, 2, 3, 4);
             for (int i = 1; i <= scope.getRequiredArgs(); i++) {
                 mv.aload(4 + i);
             }
             mv.getstatic(p(Block.class), "NULL_BLOCK"ci(Block.class));
 
             switch (scope.getRequiredArgs()) {
             case 0:
                 signature = ;
                 break;
             case 1:
                 signature = ;
                 break;
             case 2:
                 signature = ;
                 break;
             case 3:
                 signature = ;
                 break;
             }
 
             mv.invokevirtual(invokerPath"call"signature);
             mv.areturn();
             mv.end();
 
             mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "call"signaturenullnull);
         }
 
         mv.start();
 
         // save off callNumber
         mv.aload(1);
         mv.getfield(p(ThreadContext.class), "callNumber"ci(int.class));
         int callNumberIndex = -1;
         if (specificArity) {
             switch (scope.getRequiredArgs()) {
             case -1:
                 callNumberIndex =  + 1/*args*/ + 1/*block*/ + 1;
                 break;
             case 0:
                 callNumberIndex =  + 1/*block*/ + 1;
                 break;
             default:
                 callNumberIndex =  + scope.getRequiredArgs() + 1/*block*/ + 1;
             }
         } else {
             callNumberIndex =  + 1/*block*/ + 1;
         }
         mv.istore(callNumberIndex);
 
         // invoke pre method stuff
         if (!callConfig.isNoop() || .) {
             if (specificArity) {
                 invokeCallConfigPre(mvscope.getRequiredArgs(), truecallConfig);
             } else {
                 invokeCallConfigPre(mv, -1, truecallConfig);
             }
         }
 
         // pre-call trace
         int traceBoolIndex = -1;
         if (.) {
             // load and store trace enabled flag
             if (specificArity) {
                 switch (scope.getRequiredArgs()) {
                 case -1:
                     traceBoolIndex =  + 1/*args*/ + 1/*block*/ + 2;
                     break;
                 case 0:
                     traceBoolIndex =  + 1/*block*/ + 2;
                     break;
                 default:
                     traceBoolIndex =  + scope.getRequiredArgs() + 1/*block*/ + 2;
                 }
             } else {
                 traceBoolIndex =  + 1/*block*/ + 2;
             }
 
             mv.aload(1);
             mv.invokevirtual(p(ThreadContext.class), "getRuntime"sig(Ruby.class));
             mv.invokevirtual(p(Ruby.class), "hasEventHooks"sig(boolean.class));
             mv.istore(traceBoolIndex);
             // tracing pre
             invokeTraceCompiledPre(mvtraceBoolIndexfilenameline);
         }
 
         Label tryBegin = new Label();
         Label tryEnd = new Label();
         Label doFinally = new Label();
         Label doReturnFinally = new Label();
         Label doRedoFinally = new Label();
         Label catchReturnJump = new Label();
         Label catchRedoJump = new Label();
 
         boolean heapScoped = callConfig.scoping() != .;
         boolean framed = callConfig.framing() != .;
 
         if (framed || heapScoped)   mv.trycatch(tryBegintryEndcatchReturnJumpp(JumpException.ReturnJump.class));
         if (framed)                 mv.trycatch(tryBegintryEndcatchRedoJumpp(JumpException.RedoJump.class));
         if (framed || heapScoped)   mv.trycatch(tryBegintryEnddoFinallynull);
         if (framed || heapScoped)   mv.trycatch(catchReturnJumpdoReturnFinallydoFinallynull);
         if (framed)                 mv.trycatch(catchRedoJumpdoRedoFinallydoFinallynull);
         if (framed || heapScoped)   mv.label(tryBegin);
 
         // main body
         {
             mv.aload(0);
             // FIXME we want to eliminate these type casts when possible
             mv.getfield(invokerPath"$scriptObject"ci(Object.class));
             mv.checkcast(className);
             mv.aloadMany();
             if (specificArity) {
                 for (int i = 0; i < scope.getRequiredArgs(); i++) {
                     mv.aload( + i);
                 }
                 mv.aload( + scope.getRequiredArgs());
                 mv.invokestatic(classNamemethod, StandardASMCompiler.getStaticMethodSignature(classNamescope.getRequiredArgs()));
             } else {
                 mv.aloadMany();
                 mv.invokestatic(classNamemethod, StandardASMCompiler.getStaticMethodSignature(className, 4));
             }
         }
         if (framed || heapScoped) {
             mv.label(tryEnd);
         }
 
         // normal exit, perform finally and return
         {
             if (.) {
                 invokeTraceCompiledPost(mvtraceBoolIndex);
             }
             if (!callConfig.isNoop()) {
                 invokeCallConfigPost(mvcallConfig);
             }
             mv.visitInsn(ARETURN);
         }
 
         // return jump handling
         if (framed || heapScoped) {
             mv.label(catchReturnJump);
             {
                 mv.aload(0);
                 mv.swap();
                 mv.aload(1);
                 mv.swap();
                 mv.iload(callNumberIndex);
                 mv.invokevirtual("handleReturn"sig(IRubyObject.classThreadContext.classJumpException.ReturnJump.classint.class));
                 mv.label(doReturnFinally);
 
                 // finally
                 if (.) {
                     invokeTraceCompiledPost(mvtraceBoolIndex);
                 }
                 if (!callConfig.isNoop()) {
                     invokeCallConfigPost(mvcallConfig);
                 }
 
                 // return result if we're still good
                 mv.areturn();
             }
         }
 
         if (framed) {
             // redo jump handling
             mv.label(catchRedoJump);
             {
                 // clear the redo
                 mv.pop();
 
                 // get runtime, create jump error, and throw it
                 mv.aload(1);
                 mv.invokevirtual(p(ThreadContext.class), "getRuntime"sig(Ruby.class));
                 mv.invokevirtual(p(Ruby.class), "newRedoLocalJumpError"sig(RaiseException.class));
                 mv.label(doRedoFinally);
 
                 // finally
                 if (.) {
                     invokeTraceCompiledPost(mvtraceBoolIndex);
                 }
                 if (!callConfig.isNoop()) {
                     invokeCallConfigPost(mvcallConfig);
                 }
 
                 // throw redo error if we're still good
                 mv.athrow();
             }
         }
 
         // finally handling for abnormal exit
         if (framed || heapScoped) {
             mv.label(doFinally);
 
             //call post method stuff (exception raised)
             if (.) {
                 invokeTraceCompiledPost(mvtraceBoolIndex);
             }
             if (!callConfig.isNoop()) {
                 invokeCallConfigPost(mvcallConfig);
             }
 
             // rethrow exception
             mv.athrow(); // rethrow it
         }
         mv.end();
 
         return endCallOffline(cw);
     }
     
     private static class DescriptorInfo {
         private int min;
         private int max;
         private boolean frame;
         private boolean scope;
         private boolean rest;
         private boolean block;
         
         public DescriptorInfo(List<JavaMethodDescriptordescs) {
              = .;
              = 0;
              = false;
              = false;
              = false;
              = false;
             boolean first = true;
             boolean lastBlock = false;
 
             for (JavaMethodDescriptor descdescs) {
                 // make sure we don't have some methods with blocks and others without
                 // the handle generation logic can't handle such cases yet
                 if (first) {
                     first = false;
                 } else {
                     if (lastBlock != desc.hasBlock) {
                         throw new RuntimeException("Mismatched block parameters for method " + desc.declaringClassName + "." + desc.name);
                     }
                 }
                 lastBlock = desc.hasBlock;
                 
                 int specificArity = -1;
                 if (desc.hasVarArgs) {
                     if (desc.optional == 0 && !desc.rest && desc.required == 0) {
                         throw new RuntimeException("IRubyObject[] args but neither of optional or rest specified for method " + desc.declaringClassName + "." + desc.name);
                     }
                      = true;
                     if (descs.size() == 1) {
                          = -1;
                     }
                 } else {
                     if (desc.optional == 0 && !desc.rest) {
                         if (desc.required == 0) {
                             // No required specified, check actual number of required args
                             if (desc.actualRequired <= 3) {
                                 // actual required is less than 3, so we use specific arity
                                 specificArity = desc.actualRequired;
                             } else {
                                 // actual required is greater than 3, raise error (we don't support actual required > 3)
                                 throw new RuntimeException("Invalid specific-arity number of arguments (" + desc.actualRequired + ") on method " + desc.declaringClassName + "." + desc.name);
                             }
                         } else if (desc.required >= 0 && desc.required <= 3) {
                             if (desc.actualRequired != desc.required) {
                                 throw new RuntimeException("Specified required args does not match actual on method " + desc.declaringClassName + "." + desc.name);
                             }
                             specificArity = desc.required;
                         }
                     }
 
                     if (specificArity < ) {
                          = specificArity;
                     }
 
                     if (specificArity > ) {
                          = specificArity;
                     }
                 }
 
                  |= desc.anno.frame();
                  |= desc.anno.scope();
                  |= desc.hasBlock;
             }
         }
 
         @Deprecated
         public boolean isBacktrace() {
             return false;
         }
 
         public boolean isFrame() {
             return ;
         }
 
         public int getMax() {
             return ;
         }
 
         public int getMin() {
             return ;
         }
 
         public boolean isScope() {
             return ;
         }
         
         public boolean isRest() {
             return ;
         }
         
         public boolean isBlock() {
             return ;
         }
     }

    
Use code generation to provide a method handle based on an annotated Java method.

See also:
org.jruby.runtime.MethodFactory.getAnnotatedMethod
 
     public DynamicMethod getAnnotatedMethod(RubyModule implementationClassList<JavaMethodDescriptordescs) {
         JavaMethodDescriptor desc1 = descs.get(0);
         String javaMethodName = desc1.name;
         
         if (.println("Binding multiple: " + desc1.declaringClassName + "." + javaMethodName);
 
         try {
             Class c = getAnnotatedMethodClass(descs);
 
             DescriptorInfo info = new DescriptorInfo(descs);
             if (.println(" min: " + info.getMin() + ", max: " + info.getMax());
 
             JavaMethod ic = (JavaMethod)c.getConstructor(new Class[]{RubyModule.classVisibility.class}).newInstance(new Object[]{implementationClassdesc1.anno.visibility()});
 
             TypePopulator.populateMethod(
                     ic,
                     Arity.optional().getValue(),
                     javaMethodName,
                     desc1.isStatic,
                     CallConfiguration.getCallConfig(info.isFrame(), info.isScope()),
                     desc1.anno.notImplemented(),
                     desc1.getDeclaringClass(),
                     desc1.name,
                     desc1.getReturnClass(),
                     desc1.getParameterClasses());
             return ic;
         } catch(Exception e) {
             e.printStackTrace();
             throw implementationClass.getRuntime().newLoadError(e.getMessage());
         }
     }

    
Use code generation to provide a method handle based on an annotated Java method. Return the resulting generated or loaded class.

See also:
org.jruby.runtime.MethodFactory.getAnnotatedMethod
 
     public Class getAnnotatedMethodClass(List<JavaMethodDescriptordescsthrows Exception {
         JavaMethodDescriptor desc1 = descs.get(0);
 
         if (!Modifier.isPublic(desc1.getDeclaringClass().getModifiers())) {
             .warn("warning: binding non-public class {}; reflected handles won't work"desc1.declaringClassName);
         }
         
         String javaMethodName = desc1.name;
         
         if () {
             if (descs.size() > 1) {
                 .println("Binding multiple: " + desc1.declaringClassName + "." + javaMethodName);
             } else {
                 .println("Binding single: " + desc1.declaringClassName + "." + javaMethodName);
             }
         }
         
         String generatedClassName = CodegenUtils.getAnnotatedBindingClassName(javaMethodNamedesc1.declaringClassNamedesc1.isStaticdesc1.actualRequireddesc1.optionaldescs.size() > 1, desc1.anno.frame());
         if (.) {
             // in debug mode we append _DBG to class name to force it to regenerate (or use pre-generated debug version)
             generatedClassName += "_DBG";
         }
         String generatedClassPath = generatedClassName.replace('.''/');
 
         Class c = tryClass(generatedClassNamedesc1.getDeclaringClass());
         if (c == null) {
             synchronized () {
                 // try again
                 c = tryClass(generatedClassNamedesc1.getDeclaringClass());
                 if (c == null) {
                     DescriptorInfo info = new DescriptorInfo(descs);
                     if (.println("Generating " + generatedClassName + ", min: " + info.getMin() + ", max: " + info.getMax() + ", hasBlock: " + info.isBlock() + ", rest: " + info.isRest());
 
                     Class superClass = null;
                     if (info.getMin() == -1) {
                         // normal all-rest method
                         if (info.isBlock()) {
                             superClass = JavaMethod.JavaMethodNBlock.class;
                         } else {
                             superClass = JavaMethod.JavaMethodN.class;
                         }
                     } else {
                         if (info.isRest()) {
                             if (info.isBlock()) {
                                 superClass = .[info.getMin()][info.getMax()];
                             } else {
                                 superClass = .[info.getMin()][info.getMax()];
                             }
                         } else {
                             if (info.isBlock()) {
                                 superClass = .[info.getMin()][info.getMax()];
                             } else {
                                 superClass = .[info.getMin()][info.getMax()];
                             }
                         }
                     }
 
                     if (superClass == nullthrow new RuntimeException("invalid multi combination");
                     String superClassString = p(superClass);
                     int dotIndex = desc1.declaringClassName.lastIndexOf('.');
                     ClassWriter cw = createJavaMethodCtor(generatedClassPathdesc1.declaringClassName.substring(dotIndex + 1) + "$" + desc1.namesuperClassString);
 
                     addAnnotatedMethodInvoker(cw"call"superClassStringdescs);
 
                     c = endClass(cwgeneratedClassName);
                 }
             }
         }
 
         return c;
     }

    
Use code generation to provide a method handle based on an annotated Java method.

See also:
org.jruby.runtime.MethodFactory.getAnnotatedMethod
 
     public DynamicMethod getAnnotatedMethod(RubyModule implementationClassJavaMethodDescriptor desc) {
         String javaMethodName = desc.name;
 
         try {
             Class c = getAnnotatedMethodClass(Arrays.asList(desc));
 
             JavaMethod ic = (JavaMethod)c.getConstructor(new Class[]{RubyModule.classVisibility.class}).newInstance(new Object[]{implementationClassdesc.anno.visibility()});
 
             TypePopulator.populateMethod(
                     ic,
                     Arity.fromAnnotation(desc.annodesc.actualRequired).getValue(),
                     javaMethodName,
                     desc.isStatic,
                     CallConfiguration.getCallConfigByAnno(desc.anno),
                     desc.anno.notImplemented(),
                     desc.getDeclaringClass(),
                     desc.name,
                     desc.getReturnClass(),
                     desc.getParameterClasses());
             return ic;
         } catch(Exception e) {
             e.printStackTrace();
             throw implementationClass.getRuntime().newLoadError(e.getMessage());
         }
     }
 
     public static String getBlockCallbackName(String typePathStringString method) {
         return (typePathString + "$" + method).replaceAll("/""\\$");
     }
 
     public CompiledBlockCallback getBlockCallback(String methodString fileint lineObject scriptObject) {
         Class typeClass = scriptObject.getClass();
         String typePathString = p(typeClass);
         String mname = getBlockCallbackName(typePathStringmethod);
         try {
             Class c = tryBlockCallbackClass(mname);
             if (c == null) {
                 synchronized () {
                     c = tryBlockCallbackClass(mname);
                     if (c == null) {
                         if (.) {
                             .debug("no generated handle in classloader for: {}"mname);
                         }
                         byte[] bytes = getBlockCallbackOffline(methodfilelinetypePathString);
                         c = endClassWithBytes(bytesmname);
                     } else {
                         if (.) {
                             .debug("found generated handle in classloader for: {}"mname);
                         }
                     }
                 }
             }
                 
             CompiledBlockCallback ic = (CompiledBlockCallbackc.getConstructor(Object.class).newInstance(scriptObject);
             return ic;
         } catch (IllegalArgumentException e) {
             throw e;
         } catch (Exception e) {
             throw new IllegalArgumentException(e.getMessage());
         }
     }
 
     @Override
     public byte[] getBlockCallbackOffline(String methodString fileint lineString classname) {
         String mname = getBlockCallbackName(classnamemethod);
         ClassWriter cw = createBlockCtor(mnameclassname);
         SkinnyMethodAdapter mv = startBlockCall(cw);
         mv.aload(0);
         mv.getfield(mname"$scriptObject""L" + classname + ";");
         mv.aloadMany(1, 2, 3, 4);
         mv.invokestatic(classnamemethodsig(
                 IRubyObject.class"L" + classname + ";"ThreadContext.class,
                         IRubyObject.classIRubyObject.classBlock.class));
         mv.areturn();
         mv.end();
 
         mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "getFile"sig(String.class), nullnull);
         mv.start();
         mv.ldc(file);
         mv.areturn();
         mv.end();
 
         mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "getLine"sig(int.class), nullnull);
         mv.start();
         mv.ldc(line);
         mv.ireturn();
         mv.end();
 
         return endCallOffline(cw);
     }
 
     public CompiledBlockCallback19 getBlockCallback19(String methodString fileint lineObject scriptObject) {
         Class typeClass = scriptObject.getClass();
         String typePathString = p(typeClass);
         String mname = getBlockCallbackName(typePathStringmethod);
         try {
             Class c = tryBlockCallback19Class(mname);
             if (c == null) {
                 synchronized () {
                     c = tryBlockCallback19Class(mname);
                     if (c == null) {
                         if (.) {
                             .debug("no generated handle in classloader for: {}"mname);
                         }
                         byte[] bytes = getBlockCallback19Offline(methodfilelinetypePathString);
                         c = endClassWithBytes(bytesmname);
                     } else {
                         if (.) {
                             .debug("found generated handle in classloader for: {}"mname);
                         }
                     }
                 }
             }
                 
             CompiledBlockCallback19 ic = (CompiledBlockCallback19c.getConstructor(Object.class).newInstance(scriptObject);
             return ic;
         } catch (IllegalArgumentException e) {
             throw e;
         } catch (Exception e) {
             throw new IllegalArgumentException(e.getMessage());
         }
     }
 
     @Override
     public byte[] getBlockCallback19Offline(String methodString fileint lineString classname) {
         String mnamePath = getBlockCallbackName(classnamemethod);
         ClassWriter cw = createBlockCtor19(mnamePathclassname);
         SkinnyMethodAdapter mv = startBlockCall19(cw);
         mv.aload(0);
         mv.getfield(mnamePath"$scriptObject""L" + classname + ";");
         mv.aloadMany(1, 2, 3, 4);
         mv.invokestatic(classnamemethodsig(
                 IRubyObject.class"L" + classname + ";"ThreadContext.class,
                         IRubyObject.classIRubyObject[].classBlock.class));
         mv.areturn();
         mv.end();
 
         mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "getFile"sig(String.class), nullnull);
         mv.start();
         mv.ldc(file);
         mv.areturn();
         mv.end();
 
         mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "getLine"sig(int.class), nullnull);
         mv.start();
         mv.ldc(line);
         mv.ireturn();
         mv.end();
         
         return endCallOffline(cw);
     }
 
     private SkinnyMethodAdapter startBlockCall(ClassWriter cw) {
         SkinnyMethodAdapter mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_SYNTHETIC | ACC_FINAL, "call"nullnull);
 
         mv.visitCode();
         return mv;
     }
 
     private SkinnyMethodAdapter startBlockCall19(ClassWriter cw) {
         SkinnyMethodAdapter mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_SYNTHETIC | ACC_FINAL, "call"nullnull);
 
         mv.visitCode();
         return mv;
     }
 
     private ClassWriter createBlockCtor(String namePathString classname) {
         String ciClassname = "L" + classname + ";";
         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
         cw.visit(., ACC_PUBLIC + ACC_SUPER, namePathnullp(CompiledBlockCallback.class), null);
         cw.visitSource(namePathnull);
         cw.visitField(ACC_PRIVATE | ACC_FINAL, "$scriptObject"ciClassnamenullnull);
         SkinnyMethodAdapter mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "<init>"sig(.params(Object.class)), nullnull);
         mv.start();
         mv.aload(0);
         mv.invokespecial(p(CompiledBlockCallback.class), "<init>"sig(void.class));
         mv.aloadMany(0, 1);
         mv.checkcast(classname);
         mv.putfield(namePath"$scriptObject"ciClassname);
         mv.voidreturn();
         mv.end();
 
         return cw;
    }
    private ClassWriter createBlockCtor19(String namePathString classname) {
        String ciClassname = "L" + classname + ";";
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
        cw.visit(., ACC_PUBLIC + ACC_SUPER, namePathnullp(Object.class), new String[] {p(CompiledBlockCallback19.class)});
        cw.visitSource(namePathnull);
        cw.visitField(ACC_PRIVATE | ACC_FINAL, "$scriptObject"ciClassnamenullnull);
        SkinnyMethodAdapter mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "<init>"sig(.params(Object.class)), nullnull);
        mv.start();
        mv.aload(0);
        mv.invokespecial(p(Object.class), "<init>"sig(void.class));
        mv.aloadMany(0, 1);
        mv.checkcast(classname);
        mv.putfield(namePath"$scriptObject"ciClassname);
        mv.voidreturn();
        mv.end();
        return cw;
    }

    
Use code generation to provide a method handle based on an annotated Java method.

See also:
org.jruby.runtime.MethodFactory.getAnnotatedMethod
    public void prepareAnnotatedMethod(RubyModule implementationClassJavaMethod javaMethodJavaMethodDescriptor desc) {
        String javaMethodName = desc.name;
        
        javaMethod.setArity(Arity.fromAnnotation(desc.annodesc.actualRequired));
        javaMethod.setJavaName(javaMethodName);
        javaMethod.setSingleton(desc.isStatic);
        javaMethod.setCallConfig(CallConfiguration.getCallConfigByAnno(desc.anno));
    }

    
Emit code to check the arity of a call to a Java-based method.

Parameters:
jrubyMethod The annotation of the called method
method The code generator for the handle being created
    private void checkArity(JRubyMethod jrubyMethodSkinnyMethodAdapter methodint specificArity) {
        Label arityError = new Label();
        Label noArityError = new Label();
        
        switch (specificArity) {
        case 0:
        case 1:
        case 2:
        case 3:
            // for zero, one, two, three arities, JavaMethod.JavaMethod*.call(...IRubyObject[] args...) will check
            return;
        default:
            boolean checkArity = false;
            if (jrubyMethod.rest()) {
                if (jrubyMethod.required() > 0) {
                    // just confirm minimum args provided
                    method.aload();
                    method.arraylength();
                    method.ldc(jrubyMethod.