InvocationMethodFactory.java

  1. /***** BEGIN LICENSE BLOCK *****
  2.  * Version: EPL 1.0/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Eclipse Public
  5.  * License Version 1.0 (the "License"); you may not use this file
  6.  * except in compliance with the License. You may obtain a copy of
  7.  * the License at http://www.eclipse.org/legal/epl-v10.html
  8.  *
  9.  * Software distributed under the License is distributed on an "AS
  10.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  11.  * implied. See the License for the specific language governing
  12.  * rights and limitations under the License.
  13.  *
  14.  * Copyright (C) 2006 The JRuby Community <www.jruby.org>
  15.  * Copyright (C) 2006 Ola Bini <ola@ologix.com>
  16.  *
  17.  * Alternatively, the contents of this file may be used under the terms of
  18.  * either of the GNU General Public License Version 2 or later (the "GPL"),
  19.  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  20.  * in which case the provisions of the GPL or the LGPL are applicable instead
  21.  * of those above. If you wish to allow use of your version of this file only
  22.  * under the terms of either the GPL or the LGPL, and not to allow others to
  23.  * use your version of this file under the terms of the EPL, indicate your
  24.  * decision by deleting the provisions above and replace them with the notice
  25.  * and other provisions required by the GPL or the LGPL. If you do not delete
  26.  * the provisions above, a recipient may use your version of this file under
  27.  * the terms of any one of the EPL, the GPL or the LGPL.
  28.  ***** END LICENSE BLOCK *****/
  29. package org.jruby.internal.runtime.methods;

  30. import java.io.PrintWriter;
  31. import java.lang.reflect.Modifier;
  32. import java.util.Arrays;
  33. import java.util.List;

  34. import org.jruby.Ruby;
  35. import org.jruby.RubyInstanceConfig;
  36. import org.jruby.RubyKernel;
  37. import org.jruby.parser.StaticScope;
  38. import org.jruby.runtime.Helpers;
  39. import org.objectweb.asm.ClassWriter;
  40. import org.objectweb.asm.Opcodes;
  41. import org.jruby.RubyModule;
  42. import org.jruby.RubyString;
  43. import org.jruby.anno.JRubyMethod;
  44. import org.jruby.anno.JavaMethodDescriptor;
  45. import org.jruby.anno.TypePopulator;
  46. import org.jruby.compiler.impl.SkinnyMethodAdapter;
  47. import org.jruby.exceptions.JumpException;
  48. import org.jruby.exceptions.RaiseException;
  49. import org.jruby.lexer.yacc.ISourcePosition;
  50. import org.jruby.runtime.Arity;
  51. import org.jruby.runtime.Block;
  52. import org.jruby.runtime.CompiledBlockCallback;
  53. import org.jruby.runtime.CompiledBlockCallback19;
  54. import org.jruby.runtime.MethodFactory;
  55. import org.jruby.runtime.ThreadContext;
  56. import org.jruby.runtime.Visibility;
  57. import org.jruby.runtime.builtin.IRubyObject;
  58. import org.jruby.util.CodegenUtils;
  59. import static org.jruby.util.CodegenUtils.*;
  60. import static java.lang.System.*;
  61. import org.jruby.util.ClassDefininngJRubyClassLoader;
  62. import org.objectweb.asm.ClassReader;
  63. import org.objectweb.asm.Label;
  64. import org.objectweb.asm.util.CheckClassAdapter;
  65. import org.jruby.util.log.Logger;
  66. import org.jruby.util.log.LoggerFactory;

  67. /**
  68.  * In order to avoid the overhead with reflection-based method handles, this
  69.  * MethodFactory uses ASM to generate tiny invoker classes. This allows for
  70.  * better performance and more specialization per-handle than can be supported
  71.  * via reflection. It also allows optimizing away many conditionals that can
  72.  * be determined once ahead of time.
  73.  *
  74.  * When running in secured environments, this factory may not function. When
  75.  * this can be detected, MethodFactory will fall back on the reflection-based
  76.  * factory instead.
  77.  *
  78.  * @see org.jruby.runtime.MethodFactory
  79.  */
  80. public class InvocationMethodFactory extends MethodFactory implements Opcodes {

  81.     private static final Logger LOG = LoggerFactory.getLogger("InvocationMethodFactory");

  82.     private static final boolean DEBUG = false;

  83.     /** The class used for the super class of compiled Ruby method handles. */
  84.     private final static Class COMPILED_SUPER_CLASS = CompiledMethod.class;
  85.    
  86.     /** The pathname of the super class for compiled Ruby method handles. */
  87.     private final static String COMPILED_SUPER_CLASS_NAME = p(COMPILED_SUPER_CLASS);

  88.     /** The class used for the super class of compiled Ruby block handles. */
  89.     private static final Class<CompiledBlockCallback> COMPILED_BLOCK_SUPER_CLASS = CompiledBlockCallback.class;

  90.     /** The pathname of the super class for compiled Ruby block handles. */
  91.     private static final String COMPILED_BLOCK_SUPER_CLASS_NAME = p(COMPILED_BLOCK_SUPER_CLASS);

  92.     /** The interface used for compiled Ruby 1.9+ block handles. */
  93.     public static final Class<CompiledBlockCallback19> COMPILED_BLOCK_19_INTERFACE = CompiledBlockCallback19.class;

  94.     /** The pathname of the interface for compiled Ruby block handles. */
  95.     public static final String COMPILED_BLOCK_19_INTERFACE_NAME = p(COMPILED_BLOCK_19_INTERFACE);
  96.    
  97.     /** The outward call signature for compiled Ruby method handles. */
  98.     private final static String COMPILED_CALL_SIG = sig(IRubyObject.class,
  99.             params(ThreadContext.class, IRubyObject.class, RubyModule.class, String.class, IRubyObject[].class));
  100.    
  101.     /** The outward call signature for compiled Ruby method handles. */
  102.     private final static String COMPILED_CALL_SIG_BLOCK = sig(IRubyObject.class,
  103.             params(ThreadContext.class, IRubyObject.class, RubyModule.class, String.class, IRubyObject[].class, Block.class));
  104.    
  105.     /** The outward arity-zero call-with-block signature for compiled Ruby method handles. */
  106.     private final static String COMPILED_CALL_SIG_ZERO_BLOCK = sig(IRubyObject.class,
  107.             params(ThreadContext.class, IRubyObject.class, RubyModule.class, String.class, Block.class));
  108.    
  109.     /** The outward arity-zero call-with-block signature for compiled Ruby method handles. */
  110.     private final static String COMPILED_CALL_SIG_ZERO = sig(IRubyObject.class,
  111.             params(ThreadContext.class, IRubyObject.class, RubyModule.class, String.class));
  112.    
  113.     /** The outward arity-zero call-with-block signature for compiled Ruby method handles. */
  114.     private final static String COMPILED_CALL_SIG_ONE_BLOCK = sig(IRubyObject.class,
  115.             params(ThreadContext.class, IRubyObject.class, RubyModule.class, String.class, IRubyObject.class, Block.class));
  116.    
  117.     /** The outward arity-zero call-with-block signature for compiled Ruby method handles. */
  118.     private final static String COMPILED_CALL_SIG_ONE = sig(IRubyObject.class,
  119.             params(ThreadContext.class, IRubyObject.class, RubyModule.class, String.class, IRubyObject.class));
  120.    
  121.     /** The outward arity-zero call-with-block signature for compiled Ruby method handles. */
  122.     private final static String COMPILED_CALL_SIG_TWO_BLOCK = sig(IRubyObject.class,
  123.             params(ThreadContext.class, IRubyObject.class, RubyModule.class, String.class, IRubyObject.class, IRubyObject.class, Block.class));
  124.    
  125.     /** The outward arity-zero call-with-block signature for compiled Ruby method handles. */
  126.     private final static String COMPILED_CALL_SIG_TWO = sig(IRubyObject.class,
  127.             params(ThreadContext.class, IRubyObject.class, RubyModule.class, String.class, IRubyObject.class, IRubyObject.class));
  128.    
  129.     /** The outward arity-zero call-with-block signature for compiled Ruby method handles. */
  130.     private final static String COMPILED_CALL_SIG_THREE_BLOCK = sig(IRubyObject.class,
  131.             params(ThreadContext.class, IRubyObject.class, RubyModule.class, String.class, IRubyObject.class, IRubyObject.class, IRubyObject.class, Block.class));
  132.    
  133.     /** The outward arity-zero call-with-block signature for compiled Ruby method handles. */
  134.     private final static String COMPILED_CALL_SIG_THREE = sig(IRubyObject.class,
  135.             params(ThreadContext.class, IRubyObject.class, RubyModule.class, String.class, IRubyObject.class, IRubyObject.class, IRubyObject.class));

  136.     private final static String BLOCK_CALL_SIG = sig(RubyKernel.IRUBY_OBJECT, params(
  137.             ThreadContext.class, RubyKernel.IRUBY_OBJECT, IRubyObject.class, Block.class));
  138.     private final static String BLOCK_CALL_SIG19 = sig(RubyKernel.IRUBY_OBJECT, params(
  139.             ThreadContext.class, IRubyObject.class, IRubyObject[].class, Block.class));
  140.    
  141.     /** The super constructor signature for Java-based method handles. */
  142.     private final static String JAVA_SUPER_SIG = sig(Void.TYPE, params(RubyModule.class, Visibility.class));
  143.    
  144.     /** The lvar index of "this" */
  145.     public static final int THIS_INDEX = 0;
  146.    
  147.     /** The lvar index of the passed-in ThreadContext */
  148.     public static final int THREADCONTEXT_INDEX = 1;
  149.    
  150.     /** The lvar index of the method-receiving object */
  151.     public static final int RECEIVER_INDEX = 2;
  152.    
  153.     /** The lvar index of the RubyClass being invoked against */
  154.     public static final int CLASS_INDEX = 3;
  155.    
  156.     /** The lvar index method name being invoked */
  157.     public static final int NAME_INDEX = 4;
  158.    
  159.     /** The lvar index of the method args on the call */
  160.     public static final int ARGS_INDEX = 5;
  161.    
  162.     /** The lvar index of the passed-in Block on the call */
  163.     public static final int BLOCK_INDEX = 6;

  164.     /** The classloader to use for code loading */
  165.     protected final ClassDefininngJRubyClassLoader classLoader;
  166.    
  167.     /** An object to sync against when loading classes, to avoid dups */
  168.     protected final Object syncObject;
  169.    
  170.     /**
  171.      * Whether this factory has seen undefined methods already. This is used to
  172.      * detect likely method handle collisions when we expect to create a new
  173.      * handle for each call.
  174.      */
  175.     private boolean seenUndefinedClasses = false;

  176.     /**
  177.      * Whether we've informed the user that we've seen undefined methods; this
  178.      * is to avoid a flood of repetitive information.
  179.      */
  180.     private boolean haveWarnedUser = false;
  181.    
  182.     /**
  183.      * Construct a new InvocationMethodFactory using the specified classloader
  184.      * to load code. If the target classloader is not an instance of
  185.      * JRubyClassLoader, it will be wrapped with one.
  186.      *
  187.      * @param classLoader The classloader to use, or to wrap if it is not a
  188.      * JRubyClassLoader instance.
  189.      */
  190.     public InvocationMethodFactory(ClassLoader classLoader) {
  191.         // use the given classloader as our sync, regardless of whether we wrap it
  192.         this.syncObject = classLoader;
  193.        
  194.         if (classLoader instanceof ClassDefininngJRubyClassLoader) {
  195.             this.classLoader = (ClassDefininngJRubyClassLoader)classLoader;
  196.         } else {
  197.             this.classLoader = new ClassDefininngJRubyClassLoader(classLoader);
  198.         }
  199.     }

  200.     /**
  201.      * Use code generation to provide a method handle for a compiled Ruby method.
  202.      *
  203.      * @see org.jruby.runtime.MethodFactory#getCompiledMethod
  204.      */
  205.     public DynamicMethod getCompiledMethodLazily(
  206.             RubyModule implementationClass,
  207.             String rubyName,
  208.             String javaName,
  209.             Arity arity,
  210.             Visibility visibility,
  211.             StaticScope scope,
  212.             Object scriptObject,
  213.             CallConfiguration callConfig,
  214.             ISourcePosition position,
  215.             String parameterDesc,
  216.             MethodNodes methodNodes) {

  217.         return new CompiledMethod.LazyCompiledMethod(
  218.                 implementationClass,
  219.                 rubyName,
  220.                 javaName,
  221.                 arity,
  222.                 visibility,
  223.                 scope,
  224.                 scriptObject,
  225.                 callConfig,
  226.                 position,
  227.                 parameterDesc,
  228.                 new InvocationMethodFactory(classLoader),
  229.                 methodNodes);
  230.     }

  231.     public static String getCompiledCallbackName(String typePath, String method) {
  232.         return (typePath + "$" + method).replaceAll("/", "\\$");
  233.     }

  234.     /**
  235.      * Use code generation to provide a method handle for a compiled Ruby method.
  236.      *
  237.      * @see org.jruby.runtime.MethodFactory#getCompiledMethod
  238.      */
  239.     public DynamicMethod getCompiledMethod(
  240.             RubyModule implementationClass,
  241.             String rubyName,
  242.             String javaName,
  243.             Arity arity,
  244.             Visibility visibility,
  245.             StaticScope scope,
  246.             Object scriptObject,
  247.             CallConfiguration callConfig,
  248.             ISourcePosition position,
  249.             String parameterDesc,
  250.             MethodNodes methodNodes) {
  251.        
  252.         Class scriptClass = scriptObject.getClass();
  253.         String typePath = p(scriptClass);
  254.         String invokerPath = getCompiledCallbackName(typePath, javaName);
  255.         Class generatedClass = null;
  256.         boolean tryLoad = false;

  257.         try {
  258.             byte[] invokerBytes = getCompiledMethodOffline(
  259.                     rubyName,
  260.                     javaName,
  261.                     typePath,
  262.                     invokerPath,
  263.                     arity,
  264.                     scope,
  265.                     callConfig,
  266.                     position.getFile(),
  267.                     position.getLine(),
  268.                     methodNodes);
  269.             generatedClass = endCallWithBytes(invokerBytes, invokerPath);
  270.         } catch (LinkageError le) {
  271.             tryLoad = true;
  272.         } catch (SecurityException se) {
  273.             tryLoad = true;
  274.         }

  275.         if (tryLoad) {
  276.             // failed to define a new class, try loading existing
  277.             generatedClass = tryClass(invokerPath, scriptClass, COMPILED_SUPER_CLASS);
  278.             if (generatedClass == null) {
  279.                 throw implementationClass.getRuntime().newLoadError("failed to generate or load invoker for " + invokerPath);
  280.             }
  281.         }

  282.         CompiledMethod compiledMethod;
  283.         try {
  284.             compiledMethod = (CompiledMethod)generatedClass.newInstance();
  285.         } catch (Exception e) {
  286.             e.printStackTrace();
  287.             throw implementationClass.getRuntime().newLoadError(e.getMessage());
  288.         }

  289.         compiledMethod.init(implementationClass, arity, visibility, scope, scriptObject, callConfig, position, parameterDesc);

  290.         Class[] params;
  291.         if (arity.isFixed() && scope.getRequiredArgs() < 4) {
  292.             params = Helpers.getStaticMethodParams(scriptClass, scope.getRequiredArgs());
  293.         } else {
  294.             params = Helpers.getStaticMethodParams(scriptClass, 4);
  295.         }
  296.         compiledMethod.setNativeCall(scriptClass, javaName, IRubyObject.class, params, true);

  297.         return compiledMethod;
  298.     }

  299.     /**
  300.      * Use code generation to provide a method handle for a compiled Ruby method.
  301.      *
  302.      * @see org.jruby.runtime.MethodFactory#getCompiledMethod
  303.      */
  304.     @Override
  305.     public byte[] getCompiledMethodOffline(
  306.             String RubyName, String method, String className, String invokerPath, Arity arity,
  307.             StaticScope scope, CallConfiguration callConfig, String filename, int line,
  308.             MethodNodes methodNodes) {
  309.         String sup = COMPILED_SUPER_CLASS_NAME;
  310.         ClassWriter cw;
  311.         cw = createCompiledCtor(invokerPath, invokerPath, sup);
  312.         SkinnyMethodAdapter mv = null;
  313.         String signature = null;
  314.         boolean specificArity = false;

  315.         // if trace, need to at least populate a backtrace frame
  316.         if (RubyInstanceConfig.FULL_TRACE_ENABLED) {
  317.             switch (callConfig) {
  318.             case FrameNoneScopeDummy:
  319.                 callConfig = CallConfiguration.FrameBacktraceScopeDummy;
  320.                 break;
  321.             case FrameNoneScopeFull:
  322.                 callConfig = CallConfiguration.FrameBacktraceScopeFull;
  323.                 break;
  324.             case FrameNoneScopeNone:
  325.                 callConfig = CallConfiguration.FrameBacktraceScopeNone;
  326.                 break;
  327.             }
  328.         }

  329.         if (scope.getRestArg() >= 0 || scope.getOptionalArgs() > 0 || scope.getRequiredArgs() > 3) {
  330.             signature = COMPILED_CALL_SIG_BLOCK;
  331.             mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "call", signature, null, null);
  332.         } else {
  333.             specificArity = true;

  334.             mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "call", COMPILED_CALL_SIG_BLOCK, null, null);
  335.             mv.start();

  336.             // check arity
  337.             mv.aloadMany(0, 1, 4, 5); // method, context, name, args, required
  338.             mv.pushInt(scope.getRequiredArgs());
  339.             mv.invokestatic(p(JavaMethod.class), "checkArgumentCount", sig(void.class, JavaMethod.class, ThreadContext.class, String.class, IRubyObject[].class, int.class));

  340.             mv.aloadMany(0, 1, 2, 3, 4);
  341.             for (int i = 0; i < scope.getRequiredArgs(); i++) {
  342.                 mv.aload(5);
  343.                 mv.ldc(i);
  344.                 mv.arrayload();
  345.             }
  346.             mv.aload(6);

  347.             switch (scope.getRequiredArgs()) {
  348.             case 0:
  349.                 signature = COMPILED_CALL_SIG_ZERO_BLOCK;
  350.                 break;
  351.             case 1:
  352.                 signature = COMPILED_CALL_SIG_ONE_BLOCK;
  353.                 break;
  354.             case 2:
  355.                 signature = COMPILED_CALL_SIG_TWO_BLOCK;
  356.                 break;
  357.             case 3:
  358.                 signature = COMPILED_CALL_SIG_THREE_BLOCK;
  359.                 break;
  360.             }

  361.             mv.invokevirtual(invokerPath, "call", signature);
  362.             mv.areturn();
  363.             mv.end();

  364.             // Define a second version that doesn't take a block, so we have unique code paths for both cases.
  365.             switch (scope.getRequiredArgs()) {
  366.             case 0:
  367.                 signature = COMPILED_CALL_SIG_ZERO;
  368.                 break;
  369.             case 1:
  370.                 signature = COMPILED_CALL_SIG_ONE;
  371.                 break;
  372.             case 2:
  373.                 signature = COMPILED_CALL_SIG_TWO;
  374.                 break;
  375.             case 3:
  376.                 signature = COMPILED_CALL_SIG_THREE;
  377.                 break;
  378.             }
  379.             mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "call", signature, null, null);
  380.             mv.start();

  381.             mv.aloadMany(0, 1, 2, 3, 4);
  382.             for (int i = 1; i <= scope.getRequiredArgs(); i++) {
  383.                 mv.aload(4 + i);
  384.             }
  385.             mv.getstatic(p(Block.class), "NULL_BLOCK", ci(Block.class));

  386.             switch (scope.getRequiredArgs()) {
  387.             case 0:
  388.                 signature = COMPILED_CALL_SIG_ZERO_BLOCK;
  389.                 break;
  390.             case 1:
  391.                 signature = COMPILED_CALL_SIG_ONE_BLOCK;
  392.                 break;
  393.             case 2:
  394.                 signature = COMPILED_CALL_SIG_TWO_BLOCK;
  395.                 break;
  396.             case 3:
  397.                 signature = COMPILED_CALL_SIG_THREE_BLOCK;
  398.                 break;
  399.             }

  400.             mv.invokevirtual(invokerPath, "call", signature);
  401.             mv.areturn();
  402.             mv.end();

  403.             mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "call", signature, null, null);
  404.         }

  405.         mv.start();

  406.         boolean heapScoped = callConfig.scoping() != Scoping.None;
  407.         boolean framed = callConfig.framing() != Framing.None;

  408.         // save off callNumber if framed or scoped, for non-local returns
  409.         int callNumberIndex = -1;
  410.         if (framed || heapScoped) {
  411.             mv.aload(1);
  412.             mv.getfield(p(ThreadContext.class), "callNumber", ci(int.class));
  413.             if (specificArity) {
  414.                 switch (scope.getRequiredArgs()) {
  415.                 case -1:
  416.                     callNumberIndex = ARGS_INDEX + 1/*args*/ + 1/*block*/ + 1;
  417.                     break;
  418.                 case 0:
  419.                     callNumberIndex = ARGS_INDEX + 1/*block*/ + 1;
  420.                     break;
  421.                 default:
  422.                     callNumberIndex = ARGS_INDEX + scope.getRequiredArgs() + 1/*block*/ + 1;
  423.                 }
  424.             } else {
  425.                 callNumberIndex = ARGS_INDEX + 1/*block*/ + 1;
  426.             }
  427.             mv.istore(callNumberIndex);
  428.         }

  429.         // invoke pre method stuff
  430.         if (!callConfig.isNoop() || RubyInstanceConfig.FULL_TRACE_ENABLED) {
  431.             if (specificArity) {
  432.                 invokeCallConfigPre(mv, COMPILED_SUPER_CLASS_NAME, scope.getRequiredArgs(), true, callConfig);
  433.             } else {
  434.                 invokeCallConfigPre(mv, COMPILED_SUPER_CLASS_NAME, -1, true, callConfig);
  435.             }
  436.         }

  437.         // pre-call trace
  438.         int traceBoolIndex = -1;
  439.         if (RubyInstanceConfig.FULL_TRACE_ENABLED) {
  440.             // load and store trace enabled flag
  441.             if (specificArity) {
  442.                 switch (scope.getRequiredArgs()) {
  443.                 case -1:
  444.                     traceBoolIndex = ARGS_INDEX + 1/*args*/ + 1/*block*/ + 2;
  445.                     break;
  446.                 case 0:
  447.                     traceBoolIndex = ARGS_INDEX + 1/*block*/ + 2;
  448.                     break;
  449.                 default:
  450.                     traceBoolIndex = ARGS_INDEX + scope.getRequiredArgs() + 1/*block*/ + 2;
  451.                 }
  452.             } else {
  453.                 traceBoolIndex = ARGS_INDEX + 1/*block*/ + 2;
  454.             }

  455.             mv.aload(1);
  456.             mv.invokevirtual(p(ThreadContext.class), "getRuntime", sig(Ruby.class));
  457.             mv.invokevirtual(p(Ruby.class), "hasEventHooks", sig(boolean.class));
  458.             mv.istore(traceBoolIndex);
  459.             // tracing pre
  460.             invokeTraceCompiledPre(mv, COMPILED_SUPER_CLASS_NAME, traceBoolIndex, filename, line);
  461.         }

  462.         Label tryBegin = new Label();
  463.         Label tryEnd = new Label();
  464.         Label doFinally = new Label();
  465.         Label doReturnFinally = new Label();
  466.         Label doRedoFinally = new Label();
  467.         Label catchReturnJump = new Label();
  468.         Label catchRedoJump = new Label();

  469.         if (framed || heapScoped)   mv.trycatch(tryBegin, tryEnd, catchReturnJump, p(JumpException.ReturnJump.class));
  470.         if (framed)                 mv.trycatch(tryBegin, tryEnd, catchRedoJump, p(JumpException.RedoJump.class));
  471.         if (framed || heapScoped)   mv.trycatch(tryBegin, tryEnd, doFinally, null);
  472.         if (framed || heapScoped)   mv.trycatch(catchReturnJump, doReturnFinally, doFinally, null);
  473.         if (framed)                 mv.trycatch(catchRedoJump, doRedoFinally, doFinally, null);
  474.         if (framed || heapScoped)   mv.label(tryBegin);

  475.         // main body
  476.         {
  477.             mv.aload(0);
  478.             // FIXME we want to eliminate these type casts when possible
  479.             mv.getfield(invokerPath, "$scriptObject", ci(Object.class));
  480.             mv.checkcast(className);
  481.             mv.aloadMany(THREADCONTEXT_INDEX, RECEIVER_INDEX);
  482.             if (specificArity) {
  483.                 for (int i = 0; i < scope.getRequiredArgs(); i++) {
  484.                     mv.aload(ARGS_INDEX + i);
  485.                 }
  486.                 mv.aload(ARGS_INDEX + scope.getRequiredArgs());
  487.                 mv.invokestatic(className, method, Helpers.getStaticMethodSignature(className, scope.getRequiredArgs()));
  488.             } else {
  489.                 mv.aloadMany(ARGS_INDEX, BLOCK_INDEX);
  490.                 mv.invokestatic(className, method, Helpers.getStaticMethodSignature(className, 4));
  491.             }
  492.         }
  493.         if (framed || heapScoped) {
  494.             mv.label(tryEnd);
  495.         }

  496.         // normal exit, perform finally and return
  497.         {
  498.             if (RubyInstanceConfig.FULL_TRACE_ENABLED) {
  499.                 invokeTraceCompiledPost(mv, COMPILED_SUPER_CLASS_NAME, traceBoolIndex);
  500.             }
  501.             if (!callConfig.isNoop()) {
  502.                 invokeCallConfigPost(mv, COMPILED_SUPER_CLASS_NAME, callConfig);
  503.             }
  504.             mv.visitInsn(ARETURN);
  505.         }

  506.         // return jump handling
  507.         if (framed || heapScoped) {
  508.             mv.label(catchReturnJump);
  509.             {
  510.                 mv.aload(0);
  511.                 mv.swap();
  512.                 mv.aload(1);
  513.                 mv.swap();
  514.                 mv.iload(callNumberIndex);
  515.                 mv.invokevirtual(COMPILED_SUPER_CLASS_NAME, "handleReturn", sig(IRubyObject.class, ThreadContext.class, JumpException.ReturnJump.class, int.class));
  516.                 mv.label(doReturnFinally);

  517.                 // finally
  518.                 if (RubyInstanceConfig.FULL_TRACE_ENABLED) {
  519.                     invokeTraceCompiledPost(mv, COMPILED_SUPER_CLASS_NAME, traceBoolIndex);
  520.                 }
  521.                 if (!callConfig.isNoop()) {
  522.                     invokeCallConfigPost(mv, COMPILED_SUPER_CLASS_NAME, callConfig);
  523.                 }

  524.                 // return result if we're still good
  525.                 mv.areturn();
  526.             }
  527.         }

  528.         if (framed) {
  529.             // redo jump handling
  530.             mv.label(catchRedoJump);
  531.             {
  532.                 // clear the redo
  533.                 mv.pop();

  534.                 // get runtime, create jump error, and throw it
  535.                 mv.aload(1);
  536.                 mv.invokevirtual(p(ThreadContext.class), "getRuntime", sig(Ruby.class));
  537.                 mv.invokevirtual(p(Ruby.class), "newRedoLocalJumpError", sig(RaiseException.class));
  538.                 mv.label(doRedoFinally);

  539.                 // finally
  540.                 if (RubyInstanceConfig.FULL_TRACE_ENABLED) {
  541.                     invokeTraceCompiledPost(mv, COMPILED_SUPER_CLASS_NAME, traceBoolIndex);
  542.                 }
  543.                 if (!callConfig.isNoop()) {
  544.                     invokeCallConfigPost(mv, COMPILED_SUPER_CLASS_NAME, callConfig);
  545.                 }

  546.                 // throw redo error if we're still good
  547.                 mv.athrow();
  548.             }
  549.         }

  550.         // finally handling for abnormal exit
  551.         if (framed || heapScoped) {
  552.             mv.label(doFinally);

  553.             //call post method stuff (exception raised)
  554.             if (RubyInstanceConfig.FULL_TRACE_ENABLED) {
  555.                 invokeTraceCompiledPost(mv, COMPILED_SUPER_CLASS_NAME, traceBoolIndex);
  556.             }
  557.             if (!callConfig.isNoop()) {
  558.                 invokeCallConfigPost(mv, COMPILED_SUPER_CLASS_NAME, callConfig);
  559.             }

  560.             // rethrow exception
  561.             mv.athrow(); // rethrow it
  562.         }
  563.         mv.end();

  564.         return endCallOffline(cw);
  565.     }
  566.    
  567.     static class DescriptorInfo {
  568.         private int min;
  569.         private int max;
  570.         private boolean frame;
  571.         private boolean scope;
  572.         private boolean rest;
  573.         private boolean block;
  574.         private String parameterDesc;
  575.        
  576.         private static final boolean RICH_NATIVE_METHOD_PARAMETERS = false;
  577.        
  578.         public DescriptorInfo(List<JavaMethodDescriptor> descs) {
  579.             min = Integer.MAX_VALUE;
  580.             max = 0;
  581.             frame = false;
  582.             scope = false;
  583.             rest = false;
  584.             block = false;
  585.             boolean first = true;
  586.             boolean lastBlock = false;

  587.             for (JavaMethodDescriptor desc: descs) {
  588.                 // make sure we don't have some methods with blocks and others without
  589.                 // the handle generation logic can't handle such cases yet
  590.                 if (first) {
  591.                     first = false;
  592.                 } else {
  593.                     if (lastBlock != desc.hasBlock) {
  594.                         throw new RuntimeException("Mismatched block parameters for method " + desc.declaringClassName + "." + desc.name);
  595.                     }
  596.                 }
  597.                 lastBlock = desc.hasBlock;
  598.                
  599.                 int specificArity = -1;
  600.                 if (desc.hasVarArgs) {
  601.                     if (desc.optional == 0 && !desc.rest && desc.required == 0) {
  602.                         throw new RuntimeException("IRubyObject[] args but neither of optional or rest specified for method " + desc.declaringClassName + "." + desc.name);
  603.                     }
  604.                     rest = true;
  605.                     if (descs.size() == 1) {
  606.                         min = -1;
  607.                     }
  608.                 } else {
  609.                     if (desc.optional == 0 && !desc.rest) {
  610.                         if (desc.required == 0) {
  611.                             // No required specified, check actual number of required args
  612.                             if (desc.actualRequired <= 3) {
  613.                                 // actual required is less than 3, so we use specific arity
  614.                                 specificArity = desc.actualRequired;
  615.                             } else {
  616.                                 // actual required is greater than 3, raise error (we don't support actual required > 3)
  617.                                 throw new RuntimeException("Invalid specific-arity number of arguments (" + desc.actualRequired + ") on method " + desc.declaringClassName + "." + desc.name);
  618.                             }
  619.                         } else if (desc.required >= 0 && desc.required <= 3) {
  620.                             if (desc.actualRequired != desc.required) {
  621.                                 throw new RuntimeException("Specified required args does not match actual on method " + desc.declaringClassName + "." + desc.name);
  622.                             }
  623.                             specificArity = desc.required;
  624.                         }
  625.                     }

  626.                     if (specificArity < min) {
  627.                         min = specificArity;
  628.                     }

  629.                     if (specificArity > max) {
  630.                         max = specificArity;
  631.                     }
  632.                 }

  633.                 if (frame && !desc.anno.frame()) throw new RuntimeException("Unbalanced frame property on method " + desc.declaringClassName + "." + desc.name);
  634.                 if (scope && !desc.anno.scope()) throw new RuntimeException("Unbalanced scope property on method " + desc.declaringClassName + "." + desc.name);
  635.                 frame |= desc.anno.frame();
  636.                 scope |= desc.anno.scope();
  637.                 block |= desc.hasBlock;
  638.             }
  639.            
  640.             // Core methods currently only show :req's for fixed-arity or a single
  641.             // :rest if it's variable arity. I have filed a bug to improve this
  642.             // (using the skipped logic below, when the time comes) but for now
  643.             // we follow suit. See https://bugs.ruby-lang.org/issues/8088
  644.            
  645.             StringBuilder descBuilder = new StringBuilder();
  646.             if (min == max) {
  647.                 int i = 0;
  648.                 for (; i < min; i++) {
  649.                     if (i > 0) descBuilder.append(';');
  650.                     descBuilder.append("q");
  651.                 }
  652.                // variable arity
  653.             } else if (RICH_NATIVE_METHOD_PARAMETERS) {
  654.                 int i = 0;
  655.                 for (; i < min; i++) {
  656.                     if (i > 0) descBuilder.append(';');
  657.                     descBuilder.append("q");
  658.                 }

  659.                 for (; i < max; i++) {
  660.                     if (i > 0) descBuilder.append(';');
  661.                     descBuilder.append("o");
  662.                 }

  663.                 if (rest) {
  664.                     if (i > 0) descBuilder.append(';');
  665.                     descBuilder.append("r");
  666.                 }
  667.             } else {
  668.                 descBuilder.append("r");
  669.             }
  670.            
  671.             parameterDesc = descBuilder.toString();
  672.         }

  673.         @Deprecated
  674.         public boolean isBacktrace() {
  675.             return false;
  676.         }

  677.         public boolean isFrame() {
  678.             return frame;
  679.         }

  680.         public int getMax() {
  681.             return max;
  682.         }

  683.         public int getMin() {
  684.             return min;
  685.         }

  686.         public boolean isScope() {
  687.             return scope;
  688.         }
  689.        
  690.         public boolean isRest() {
  691.             return rest;
  692.         }
  693.        
  694.         public boolean isBlock() {
  695.             return block;
  696.         }
  697.        
  698.         public String getParameterDesc() {
  699.             return parameterDesc;
  700.         }
  701.     }

  702.     /**
  703.      * Use code generation to provide a method handle based on an annotated Java
  704.      * method.
  705.      *
  706.      * @see org.jruby.runtime.MethodFactory#getAnnotatedMethod
  707.      */
  708.     public DynamicMethod getAnnotatedMethod(RubyModule implementationClass, List<JavaMethodDescriptor> descs) {
  709.         JavaMethodDescriptor desc1 = descs.get(0);
  710.         JRubyMethod anno = desc1.anno;
  711.         String javaMethodName = desc1.name;
  712.        
  713.         if (DEBUG) out.println("Binding multiple: " + desc1.declaringClassName + "." + javaMethodName);

  714.         try {
  715.             Class c = getAnnotatedMethodClass(descs);

  716.             DescriptorInfo info = new DescriptorInfo(descs);
  717.             if (DEBUG) out.println(" min: " + info.getMin() + ", max: " + info.getMax());

  718.             JavaMethod ic = (JavaMethod)c.getConstructor(new Class[]{RubyModule.class, Visibility.class}).newInstance(new Object[]{implementationClass, desc1.anno.visibility()});

  719.             TypePopulator.populateMethod(
  720.                     ic,
  721.                     Arity.optional().getValue(),
  722.                     javaMethodName,
  723.                     desc1.isStatic,
  724.                     CallConfiguration.getCallConfigByAnno(anno),
  725.                     desc1.anno.notImplemented(),
  726.                     desc1.getDeclaringClass(),
  727.                     desc1.name,
  728.                     desc1.getReturnClass(),
  729.                     desc1.getParameterClasses(),
  730.                     CallConfiguration.getCallerCallConfigByAnno(anno));
  731.             return ic;
  732.         } catch(Exception e) {
  733.             e.printStackTrace();
  734.             throw implementationClass.getRuntime().newLoadError(e.getMessage());
  735.         }
  736.     }

  737.     /**
  738.      * Use code generation to provide a method handle based on an annotated Java
  739.      * method. Return the resulting generated or loaded class.
  740.      *
  741.      * @see org.jruby.runtime.MethodFactory#getAnnotatedMethod
  742.      */
  743.     public Class getAnnotatedMethodClass(List<JavaMethodDescriptor> descs) throws Exception {
  744.         JavaMethodDescriptor desc1 = descs.get(0);

  745.         if (!Modifier.isPublic(desc1.getDeclaringClass().getModifiers())) {
  746.             LOG.warn("warning: binding non-public class {}; reflected handles won't work", desc1.declaringClassName);
  747.         }
  748.        
  749.         String javaMethodName = desc1.name;
  750.        
  751.         if (DEBUG) {
  752.             if (descs.size() > 1) {
  753.                 out.println("Binding multiple: " + desc1.declaringClassName + "." + javaMethodName);
  754.             } else {
  755.                 out.println("Binding single: " + desc1.declaringClassName + "." + javaMethodName);
  756.             }
  757.         }
  758.        
  759.         String generatedClassName = CodegenUtils.getAnnotatedBindingClassName(javaMethodName, desc1.declaringClassName, desc1.isStatic, desc1.actualRequired, desc1.optional, descs.size() > 1, desc1.anno.frame());
  760.         if (RubyInstanceConfig.FULL_TRACE_ENABLED) {
  761.             // in debug mode we append _DBG to class name to force it to regenerate (or use pre-generated debug version)
  762.             generatedClassName += "_DBG";
  763.         }
  764.         String generatedClassPath = generatedClassName.replace('.', '/');

  765.         DescriptorInfo info = new DescriptorInfo(descs);

  766.         Class superclass = determineSuperclass(info);

  767.         Class c = tryClass(generatedClassName, desc1.getDeclaringClass(), superclass);
  768.         if (c == null) {
  769.             synchronized (syncObject) {
  770.                 // try again
  771.                 c = tryClass(generatedClassName, desc1.getDeclaringClass(), superclass);
  772.                 if (c == null) {
  773.                     if (DEBUG) out.println("Generating " + generatedClassName + ", min: " + info.getMin() + ", max: " + info.getMax() + ", hasBlock: " + info.isBlock() + ", rest: " + info.isRest());

  774.                     String superClassString = p(superclass);
  775.                    
  776.                     ClassWriter cw = createJavaMethodCtor(generatedClassPath, superClassString, info.getParameterDesc());

  777.                     addAnnotatedMethodInvoker(cw, "call", superClassString, descs);

  778.                     c = endClass(cw, generatedClassName);
  779.                 }
  780.             }
  781.         }

  782.         return c;
  783.     }

  784.     private Class determineSuperclass(DescriptorInfo info) {
  785.         Class superClass;
  786.         if (info.getMin() == -1) {
  787.             // normal all-rest method
  788.             if (info.isBlock()) {
  789.                 superClass = JavaMethod.JavaMethodNBlock.class;
  790.             } else {
  791.                 superClass = JavaMethod.JavaMethodN.class;
  792.             }
  793.         } else {
  794.             if (info.isRest()) {
  795.                 if (info.isBlock()) {
  796.                     superClass = JavaMethod.BLOCK_REST_METHODS[info.getMin()][info.getMax()];
  797.                 } else {
  798.                     superClass = JavaMethod.REST_METHODS[info.getMin()][info.getMax()];
  799.                 }
  800.             } else {
  801.                 if (info.isBlock()) {
  802.                     superClass = JavaMethod.BLOCK_METHODS[info.getMin()][info.getMax()];
  803.                 } else {
  804.                     superClass = JavaMethod.METHODS[info.getMin()][info.getMax()];
  805.                 }
  806.             }
  807.         }

  808.         if (superClass == null) throw new RuntimeException("invalid multi combination");
  809.         return superClass;
  810.     }

  811.     /**
  812.      * Use code generation to provide a method handle based on an annotated Java
  813.      * method.
  814.      *
  815.      * @see org.jruby.runtime.MethodFactory#getAnnotatedMethod
  816.      */
  817.     public DynamicMethod getAnnotatedMethod(RubyModule implementationClass, JavaMethodDescriptor desc) {
  818.         String javaMethodName = desc.name;

  819.         try {
  820.             Class c = getAnnotatedMethodClass(Arrays.asList(desc));

  821.             JavaMethod ic = (JavaMethod)c.getConstructor(new Class[]{RubyModule.class, Visibility.class}).newInstance(new Object[]{implementationClass, desc.anno.visibility()});

  822.             TypePopulator.populateMethod(
  823.                     ic,
  824.                     Arity.fromAnnotation(desc.anno, desc.actualRequired).getValue(),
  825.                     javaMethodName,
  826.                     desc.isStatic,
  827.                     CallConfiguration.getCallConfigByAnno(desc.anno),
  828.                     desc.anno.notImplemented(),
  829.                     desc.getDeclaringClass(),
  830.                     desc.name,
  831.                     desc.getReturnClass(),
  832.                     desc.getParameterClasses(),
  833.                     CallConfiguration.getCallerCallConfigByAnno(desc.anno));
  834.             return ic;
  835.         } catch(Exception e) {
  836.             e.printStackTrace();
  837.             throw implementationClass.getRuntime().newLoadError(e.getMessage());
  838.         }
  839.     }

  840.     public static String getBlockCallbackName(String typePathString, String method) {
  841.         return (typePathString + "$" + method).replaceAll("/", "\\$");
  842.     }

  843.     public CompiledBlockCallback getBlockCallback(String method, String file, int line, Object scriptObject) {
  844.         Class typeClass = scriptObject.getClass();
  845.         String typePathString = p(typeClass);
  846.         String mname = getBlockCallbackName(typePathString, method);
  847.         try {
  848.             Class c = tryBlockCallbackClass(mname, COMPILED_BLOCK_SUPER_CLASS);
  849.             if (c == null) {
  850.                 synchronized (syncObject) {
  851.                     c = tryBlockCallbackClass(mname, COMPILED_BLOCK_SUPER_CLASS);
  852.                     if (c == null) {
  853.                         if (RubyInstanceConfig.JIT_LOADING_DEBUG) {
  854.                             LOG.debug("no generated handle in classloader for: {}", mname);
  855.                         }
  856.                         byte[] bytes = getBlockCallbackOffline(method, file, line, typePathString);
  857.                         c = endClassWithBytes(bytes, mname);
  858.                     } else {
  859.                         if (RubyInstanceConfig.JIT_LOADING_DEBUG) {
  860.                             LOG.debug("found generated handle in classloader for: {}", mname);
  861.                         }
  862.                     }
  863.                 }
  864.             }
  865.                
  866.             CompiledBlockCallback ic = (CompiledBlockCallback) c.getConstructor(Object.class).newInstance(scriptObject);
  867.             return ic;
  868.         } catch (IllegalArgumentException e) {
  869.             throw e;
  870.         } catch (Exception e) {
  871.             throw new IllegalArgumentException(e.getMessage());
  872.         }
  873.     }

  874.     @Override
  875.     public byte[] getBlockCallbackOffline(String method, String file, int line, String classname) {
  876.         String mname = getBlockCallbackName(classname, method);
  877.         ClassWriter cw = createBlockCtor(mname, classname);
  878.         SkinnyMethodAdapter mv = startBlockCall(cw);
  879.         mv.aload(0);
  880.         mv.getfield(mname, "$scriptObject", "L" + classname + ";");
  881.         mv.aloadMany(1, 2, 3, 4);
  882.         mv.invokestatic(classname, method, sig(
  883.                 IRubyObject.class, "L" + classname + ";", ThreadContext.class,
  884.                         IRubyObject.class, IRubyObject.class, Block.class));
  885.         mv.areturn();
  886.         mv.end();

  887.         mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "getFile", sig(String.class), null, null);
  888.         mv.start();
  889.         mv.ldc(file);
  890.         mv.areturn();
  891.         mv.end();

  892.         mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "getLine", sig(int.class), null, null);
  893.         mv.start();
  894.         mv.ldc(line);
  895.         mv.ireturn();
  896.         mv.end();

  897.         return endCallOffline(cw);
  898.     }

  899.     public CompiledBlockCallback19 getBlockCallback19(String method, String file, int line, Object scriptObject) {
  900.         Class typeClass = scriptObject.getClass();
  901.         String typePathString = p(typeClass);
  902.         String mname = getBlockCallbackName(typePathString, method);
  903.         try {
  904.             Class c = tryBlockCallback19Class(mname, COMPILED_BLOCK_19_INTERFACE);
  905.             if (c == null) {
  906.                 synchronized (syncObject) {
  907.                     c = tryBlockCallback19Class(mname, COMPILED_BLOCK_19_INTERFACE);
  908.                     if (c == null) {
  909.                         if (RubyInstanceConfig.JIT_LOADING_DEBUG) {
  910.                             LOG.debug("no generated handle in classloader for: {}", mname);
  911.                         }
  912.                         byte[] bytes = getBlockCallback19Offline(method, file, line, typePathString);
  913.                         c = endClassWithBytes(bytes, mname);
  914.                     } else {
  915.                         if (RubyInstanceConfig.JIT_LOADING_DEBUG) {
  916.                             LOG.debug("found generated handle in classloader for: {}", mname);
  917.                         }
  918.                     }
  919.                 }
  920.             }
  921.                
  922.             CompiledBlockCallback19 ic = (CompiledBlockCallback19) c.getConstructor(Object.class).newInstance(scriptObject);
  923.             return ic;
  924.         } catch (IllegalArgumentException e) {
  925.             throw e;
  926.         } catch (Exception e) {
  927.             throw new IllegalArgumentException(e.getMessage());
  928.         }
  929.     }

  930.     @Override
  931.     public byte[] getBlockCallback19Offline(String method, String file, int line, String classname) {
  932.         String mnamePath = getBlockCallbackName(classname, method);
  933.         ClassWriter cw = createBlockCtor19(mnamePath, classname);
  934.         SkinnyMethodAdapter mv = startBlockCall19(cw);
  935.         mv.aload(0);
  936.         mv.getfield(mnamePath, "$scriptObject", "L" + classname + ";");
  937.         mv.aloadMany(1, 2, 3, 4);
  938.         mv.invokestatic(classname, method, sig(
  939.                 IRubyObject.class, "L" + classname + ";", ThreadContext.class,
  940.                         IRubyObject.class, IRubyObject[].class, Block.class));
  941.         mv.areturn();
  942.         mv.end();

  943.         mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "getFile", sig(String.class), null, null);
  944.         mv.start();
  945.         mv.ldc(file);
  946.         mv.areturn();
  947.         mv.end();

  948.         mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "getLine", sig(int.class), null, null);
  949.         mv.start();
  950.         mv.ldc(line);
  951.         mv.ireturn();
  952.         mv.end();
  953.        
  954.         return endCallOffline(cw);
  955.     }

  956.     private SkinnyMethodAdapter startBlockCall(ClassWriter cw) {
  957.         SkinnyMethodAdapter mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_SYNTHETIC | ACC_FINAL, "call", BLOCK_CALL_SIG, null, null);

  958.         mv.visitCode();
  959.         return mv;
  960.     }

  961.     private SkinnyMethodAdapter startBlockCall19(ClassWriter cw) {
  962.         SkinnyMethodAdapter mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_SYNTHETIC | ACC_FINAL, "call", BLOCK_CALL_SIG19, null, null);

  963.         mv.visitCode();
  964.         return mv;
  965.     }

  966.     private ClassWriter createBlockCtor(String namePath, String classname) {
  967.         String ciClassname = "L" + classname + ";";
  968.         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
  969.         cw.visit(RubyInstanceConfig.JAVA_VERSION, ACC_PUBLIC + ACC_SUPER, namePath, null, COMPILED_BLOCK_SUPER_CLASS_NAME, null);
  970.         cw.visitSource(namePath, null);
  971.         cw.visitField(ACC_PRIVATE | ACC_FINAL, "$scriptObject", ciClassname, null, null);
  972.         SkinnyMethodAdapter mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "<init>", sig(Void.TYPE, params(Object.class)), null, null);
  973.         mv.start();
  974.         mv.aload(0);
  975.         mv.invokespecial(p(CompiledBlockCallback.class), "<init>", sig(void.class));
  976.         mv.aloadMany(0, 1);
  977.         mv.checkcast(classname);
  978.         mv.putfield(namePath, "$scriptObject", ciClassname);
  979.         mv.voidreturn();
  980.         mv.end();

  981.         return cw;
  982.     }

  983.     private ClassWriter createBlockCtor19(String namePath, String classname) {
  984.         String ciClassname = "L" + classname + ";";
  985.         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
  986.         cw.visit(RubyInstanceConfig.JAVA_VERSION, ACC_PUBLIC + ACC_SUPER, namePath, null, p(Object.class), new String[] {COMPILED_BLOCK_19_INTERFACE_NAME});
  987.         cw.visitSource(namePath, null);
  988.         cw.visitField(ACC_PRIVATE | ACC_FINAL, "$scriptObject", ciClassname, null, null);
  989.         SkinnyMethodAdapter mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "<init>", sig(Void.TYPE, params(Object.class)), null, null);
  990.         mv.start();
  991.         mv.aload(0);
  992.         mv.invokespecial(p(Object.class), "<init>", sig(void.class));
  993.         mv.aloadMany(0, 1);
  994.         mv.checkcast(classname);
  995.         mv.putfield(namePath, "$scriptObject", ciClassname);
  996.         mv.voidreturn();
  997.         mv.end();

  998.         return cw;
  999.     }

  1000.     /**
  1001.      * Use code generation to provide a method handle based on an annotated Java
  1002.      * method.
  1003.      *
  1004.      * @see org.jruby.runtime.MethodFactory#getAnnotatedMethod
  1005.      */
  1006.     public void prepareAnnotatedMethod(RubyModule implementationClass, JavaMethod javaMethod, JavaMethodDescriptor desc) {
  1007.         String javaMethodName = desc.name;
  1008.        
  1009.         javaMethod.setArity(Arity.fromAnnotation(desc.anno, desc.actualRequired));
  1010.         javaMethod.setJavaName(javaMethodName);
  1011.         javaMethod.setSingleton(desc.isStatic);
  1012.         javaMethod.setCallConfig(CallConfiguration.getCallConfigByAnno(desc.anno));
  1013.     }

  1014.     /**
  1015.      * Emit code to check the arity of a call to a Java-based method.
  1016.      *
  1017.      * @param jrubyMethod The annotation of the called method
  1018.      * @param method The code generator for the handle being created
  1019.      */
  1020.     private void checkArity(JRubyMethod jrubyMethod, SkinnyMethodAdapter method, int specificArity) {
  1021.         Label arityError = new Label();
  1022.         Label noArityError = new Label();
  1023.        
  1024.         switch (specificArity) {
  1025.         case 0:
  1026.         case 1:
  1027.         case 2:
  1028.         case 3:
  1029.             // for zero, one, two, three arities, JavaMethod.JavaMethod*.call(...IRubyObject[] args...) will check
  1030.             return;
  1031.         default:
  1032.             boolean checkArity = false;
  1033.             if (jrubyMethod.rest()) {
  1034.                 if (jrubyMethod.required() > 0) {
  1035.                     // just confirm minimum args provided
  1036.                     method.aload(ARGS_INDEX);
  1037.                     method.arraylength();
  1038.                     method.ldc(jrubyMethod.required());
  1039.                     method.if_icmplt(arityError);
  1040.                     checkArity = true;
  1041.                 }
  1042.             } else if (jrubyMethod.optional() > 0) {
  1043.                 if (jrubyMethod.required() > 0) {
  1044.                     // confirm minimum args provided
  1045.                     method.aload(ARGS_INDEX);
  1046.                     method.arraylength();
  1047.                     method.ldc(jrubyMethod.required());
  1048.                     method.if_icmplt(arityError);
  1049.                 }

  1050.                 // confirm maximum not greater than optional
  1051.                 method.aload(ARGS_INDEX);
  1052.                 method.arraylength();
  1053.                 method.ldc(jrubyMethod.required() + jrubyMethod.optional());
  1054.                 method.if_icmpgt(arityError);
  1055.                 checkArity = true;
  1056.             } else {
  1057.                 // just confirm args length == required
  1058.                 method.aload(ARGS_INDEX);
  1059.                 method.arraylength();
  1060.                 method.ldc(jrubyMethod.required());
  1061.                 method.if_icmpne(arityError);
  1062.                 checkArity = true;
  1063.             }

  1064.             if (checkArity) {
  1065.                 method.go_to(noArityError);

  1066.                 // Raise an error if arity does not match requirements
  1067.                 method.label(arityError);
  1068.                 method.aload(THREADCONTEXT_INDEX);
  1069.                 method.invokevirtual(p(ThreadContext.class), "getRuntime", sig(Ruby.class));
  1070.                 method.aload(ARGS_INDEX);
  1071.                 method.ldc(jrubyMethod.required());
  1072.                 method.ldc(jrubyMethod.required() + jrubyMethod.optional());
  1073.                 method.invokestatic(p(Arity.class), "checkArgumentCount", sig(int.class, Ruby.class, IRubyObject[].class, int.class, int.class));
  1074.                 method.pop();

  1075.                 method.label(noArityError);
  1076.             }
  1077.         }
  1078.     }

  1079.     private ClassWriter createCompiledCtor(String namePath, String shortPath, String sup) {
  1080.         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
  1081.         cw.visit(RubyInstanceConfig.JAVA_VERSION, ACC_PUBLIC + ACC_SUPER, namePath, null, sup, null);
  1082.         cw.visitSource(shortPath, null);
  1083.         SkinnyMethodAdapter mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "<init>", "()V", null, null);
  1084.         mv.visitCode();
  1085.         mv.aload(0);
  1086.         mv.visitMethodInsn(INVOKESPECIAL, sup, "<init>", "()V");
  1087.         mv.voidreturn();
  1088.         mv.end();

  1089.         return cw;
  1090.     }

  1091.     private ClassWriter createJavaMethodCtor(String namePath, String sup, String parameterDesc) throws Exception {
  1092.         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
  1093.         String sourceFile = namePath.substring(namePath.lastIndexOf('/') + 1) + ".gen";
  1094.         cw.visit(RubyInstanceConfig.JAVA_VERSION, ACC_PUBLIC + ACC_SUPER, namePath, null, sup, null);
  1095.         cw.visitSource(sourceFile, null);
  1096.         SkinnyMethodAdapter mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "<init>", JAVA_SUPER_SIG, null, null);
  1097.         mv.start();
  1098.         mv.aloadMany(0, 1, 2);
  1099.         mv.visitMethodInsn(INVOKESPECIAL, sup, "<init>", JAVA_SUPER_SIG);
  1100.         mv.aload(0);
  1101.         mv.ldc(parameterDesc);
  1102.         mv.invokevirtual(p(JavaMethod.class), "setParameterDesc", sig(void.class, String.class));
  1103.         mv.voidreturn();
  1104.         mv.end();
  1105.        
  1106.         return cw;
  1107.     }

  1108.     private void invokeCallConfigPre(SkinnyMethodAdapter mv, String superClass, int specificArity, boolean block, CallConfiguration callConfig) {
  1109.         // invoke pre method stuff
  1110.         if (callConfig.isNoop()) return;

  1111.         prepareForPre(mv, specificArity, block, callConfig);
  1112.         mv.invokevirtual(superClass, getPreMethod(callConfig), getPreSignature(callConfig));
  1113.     }

  1114.     private void invokeCallConfigPost(SkinnyMethodAdapter mv, String superClass, CallConfiguration callConfig) {
  1115.         if (callConfig.isNoop()) return;

  1116.         mv.aload(1);
  1117.         mv.invokestatic(superClass, getPostMethod(callConfig), sig(void.class, params(ThreadContext.class)));
  1118.     }

  1119.     private void prepareForPre(SkinnyMethodAdapter mv, int specificArity, boolean block, CallConfiguration callConfig) {
  1120.         if (callConfig.isNoop()) return;
  1121.        
  1122.         mv.aloadMany(0, THREADCONTEXT_INDEX);
  1123.        
  1124.         switch (callConfig.framing()) {
  1125.         case Full:
  1126.             mv.aloadMany(RECEIVER_INDEX, NAME_INDEX); // self, name
  1127.             loadBlockForPre(mv, specificArity, block);
  1128.             break;
  1129.         case Backtrace:
  1130.             mv.aload(NAME_INDEX); // name
  1131.             break;
  1132.         case None:
  1133.             break;
  1134.         default: throw new RuntimeException("Unknown call configuration");
  1135.         }
  1136.     }

  1137.     private String getPreMethod(CallConfiguration callConfig) {
  1138.         switch (callConfig) {
  1139.         case FrameFullScopeFull: return "preFrameAndScope";
  1140.         case FrameFullScopeDummy: return "preFrameAndDummyScope";
  1141.         case FrameFullScopeNone: return "preFrameOnly";
  1142.         case FrameBacktraceScopeFull: return "preBacktraceAndScope";
  1143.         case FrameBacktraceScopeDummy: return "preBacktraceDummyScope";
  1144.         case FrameBacktraceScopeNone:  return "preBacktraceOnly";
  1145.         case FrameNoneScopeFull: return "preScopeOnly";
  1146.         case FrameNoneScopeDummy: return "preNoFrameDummyScope";
  1147.         case FrameNoneScopeNone: return "preNoop";
  1148.         default: throw new RuntimeException("Unknown call configuration");
  1149.         }
  1150.     }

  1151.     private String getPreSignature(CallConfiguration callConfig) {
  1152.         switch (callConfig) {
  1153.         case FrameFullScopeFull: return sig(void.class, params(ThreadContext.class, IRubyObject.class, String.class, Block.class));
  1154.         case FrameFullScopeDummy: return sig(void.class, params(ThreadContext.class, IRubyObject.class, String.class, Block.class));
  1155.         case FrameFullScopeNone: return sig(void.class, params(ThreadContext.class, IRubyObject.class, String.class, Block.class));
  1156.         case FrameBacktraceScopeFull: return sig(void.class, params(ThreadContext.class, String.class));
  1157.         case FrameBacktraceScopeDummy: return sig(void.class, params(ThreadContext.class, String.class));
  1158.         case FrameBacktraceScopeNone:  return sig(void.class, params(ThreadContext.class, String.class));
  1159.         case FrameNoneScopeFull: return sig(void.class, params(ThreadContext.class));
  1160.         case FrameNoneScopeDummy: return sig(void.class, params(ThreadContext.class));
  1161.         case FrameNoneScopeNone: return sig(void.class);
  1162.         default: throw new RuntimeException("Unknown call configuration");
  1163.         }
  1164.     }

  1165.     public static String getPostMethod(CallConfiguration callConfig) {
  1166.         switch (callConfig) {
  1167.         case FrameFullScopeFull: return "postFrameAndScope";
  1168.         case FrameFullScopeDummy: return "postFrameAndScope";
  1169.         case FrameFullScopeNone: return "postFrameOnly";
  1170.         case FrameBacktraceScopeFull: return "postBacktraceAndScope";
  1171.         case FrameBacktraceScopeDummy: return "postBacktraceDummyScope";
  1172.         case FrameBacktraceScopeNone:  return "postBacktraceOnly";
  1173.         case FrameNoneScopeFull: return "postScopeOnly";
  1174.         case FrameNoneScopeDummy: return "postNoFrameDummyScope";
  1175.         case FrameNoneScopeNone: return "postNoop";
  1176.         default: throw new RuntimeException("Unknown call configuration");
  1177.         }
  1178.     }

  1179.     private void loadArguments(SkinnyMethodAdapter mv, JavaMethodDescriptor desc, int specificArity) {
  1180.         switch (specificArity) {
  1181.         default:
  1182.         case -1:
  1183.             mv.aload(ARGS_INDEX);
  1184.             break;
  1185.         case 0:
  1186.             // no args
  1187.             break;
  1188.         case 1:
  1189.             loadArgumentWithCast(mv, 1, desc.argumentTypes[0]);
  1190.             break;
  1191.         case 2:
  1192.             loadArgumentWithCast(mv, 1, desc.argumentTypes[0]);
  1193.             loadArgumentWithCast(mv, 2, desc.argumentTypes[1]);
  1194.             break;
  1195.         case 3:
  1196.             loadArgumentWithCast(mv, 1, desc.argumentTypes[0]);
  1197.             loadArgumentWithCast(mv, 2, desc.argumentTypes[1]);
  1198.             loadArgumentWithCast(mv, 3, desc.argumentTypes[2]);
  1199.             break;
  1200.         }
  1201.     }

  1202.     private void loadArgumentWithCast(SkinnyMethodAdapter mv, int argNumber, Class coerceType) {
  1203.         mv.aload(ARGS_INDEX + (argNumber - 1));
  1204.         if (coerceType != IRubyObject.class && coerceType != IRubyObject[].class) {
  1205.             if (coerceType == RubyString.class) {
  1206.                 mv.invokeinterface(p(IRubyObject.class), "convertToString", sig(RubyString.class));
  1207.             } else {
  1208.                 throw new RuntimeException("Unknown coercion target: " + coerceType);
  1209.             }
  1210.         }
  1211.     }

  1212.     /** load block argument for pre() call.  Since we have fixed-arity call
  1213.      * paths we need calculate where the last var holding the block is.
  1214.      *
  1215.      * is we don't have a block we setup NULL_BLOCK as part of our null pattern
  1216.      * strategy (we do not allow null in any field which accepts block).
  1217.      */
  1218.     private void loadBlockForPre(SkinnyMethodAdapter mv, int specificArity, boolean getsBlock) {
  1219.         if (!getsBlock) {            // No block so load null block instance
  1220.             mv.getstatic(p(Block.class), "NULL_BLOCK", ci(Block.class));
  1221.             return;
  1222.         }

  1223.         loadBlock(mv, specificArity, getsBlock);
  1224.     }

  1225.     /** load the block argument from the correct position.  Since we have fixed-
  1226.      * arity call paths we need to calculate where the last var holding the
  1227.      * block is.
  1228.      *
  1229.      * If we don't have a block then this does nothing.
  1230.      */
  1231.     private void loadBlock(SkinnyMethodAdapter mv, int specificArity, boolean getsBlock) {
  1232.         if (!getsBlock) return;         // No block so nothing more to do
  1233.        
  1234.         switch (specificArity) {        // load block since it accepts a block
  1235.         case 0: case 1: case 2: case 3: // Fixed arities signatures
  1236.             mv.aload(BLOCK_INDEX - 1 + specificArity);
  1237.             break;
  1238.         default: case -1:
  1239.             mv.aload(BLOCK_INDEX);      // Generic arity signature
  1240.             break;
  1241.         }
  1242.     }

  1243.     private void loadReceiver(String typePath, JavaMethodDescriptor desc, SkinnyMethodAdapter mv) {
  1244.         // load target for invocations
  1245.         if (Modifier.isStatic(desc.modifiers)) {
  1246.             if (desc.hasContext) {
  1247.                 mv.aload(THREADCONTEXT_INDEX);
  1248.             }
  1249.            
  1250.             // load self object as IRubyObject, for recv param
  1251.             mv.aload(RECEIVER_INDEX);
  1252.         } else {
  1253.             // load receiver as original type for virtual invocation
  1254.             mv.aload(RECEIVER_INDEX);
  1255.             mv.checkcast(typePath);
  1256.            
  1257.             if (desc.hasContext) {
  1258.                 mv.aload(THREADCONTEXT_INDEX);
  1259.             }
  1260.         }
  1261.     }

  1262.     private Class tryClass(String name, Class targetClass, Class expectedSuperclass) {
  1263.         Class c;
  1264.         try {
  1265.             if (classLoader == null) {
  1266.                 c = Class.forName(name, true, classLoader);
  1267.             } else {
  1268.                 c = classLoader.loadClass(name);
  1269.             }
  1270.         } catch(Exception e) {
  1271.             seenUndefinedClasses = true;
  1272.             return null;
  1273.         }

  1274.         // For JRUBY-5038, ensure loaded class has superclass from same classloader as current JRuby
  1275.         if (c.getSuperclass() == expectedSuperclass) {
  1276.             if (seenUndefinedClasses && !haveWarnedUser) {
  1277.                 haveWarnedUser = true;
  1278.                 System.err.println("WARNING: while creating new bindings for " + targetClass + ",\n" +
  1279.                         "found an existing binding; you may want to run a clean build.");
  1280.             }

  1281.             return c;
  1282.         } else {
  1283.             seenUndefinedClasses = true;
  1284.             return null;
  1285.         }
  1286.     }

  1287.     private Class tryBlockCallbackClass(String name, Class expectedSuperclass) {
  1288.         try {
  1289.             Class c = classLoader.loadClass(name);

  1290.             // For JRUBY-5038, ensure loaded class has superclass from same classloader as current JRuby
  1291.             if (c.getSuperclass() == expectedSuperclass) {
  1292.                 return c;
  1293.             } else {
  1294.                 return null;
  1295.             }
  1296.         } catch (Exception e) {
  1297.             return null;
  1298.         }
  1299.     }

  1300.     private Class tryBlockCallback19Class(String name, Class expectedInterface) {
  1301.         try {
  1302.             Class c = classLoader.loadClass(name);

  1303.             // For JRUBY-5038, ensure loaded class has superclass from same classloader as current JRuby
  1304.             Class<?>[] interfaces = c.getInterfaces();
  1305.             if (interfaces.length == 1 && interfaces[0] == expectedInterface) {
  1306.                 return c;
  1307.             } else {
  1308.                 return null;
  1309.             }
  1310.         } catch (Exception e) {
  1311.             return null;
  1312.         }
  1313.     }

  1314.     protected Class endCall(ClassWriter cw, String name) {
  1315.         return endClass(cw, name);
  1316.     }

  1317.     protected Class endCallWithBytes(byte[] classBytes, String name) {
  1318.         return endClassWithBytes(classBytes, name);
  1319.     }

  1320.     protected byte[] endCallOffline(ClassWriter cw) {
  1321.         return endClassOffline(cw);
  1322.     }

  1323.     protected Class endClass(ClassWriter cw, String name) {
  1324.         cw.visitEnd();
  1325.         byte[] code = cw.toByteArray();
  1326.         if (DEBUG) CheckClassAdapter.verify(new ClassReader(code), false, new PrintWriter(System.err));
  1327.          
  1328.         return classLoader.defineClass(name, code);
  1329.     }

  1330.     protected Class endClassWithBytes(byte[] code, String name) {
  1331.         return classLoader.defineClass(name, code);
  1332.     }

  1333.     protected byte[] endClassOffline(ClassWriter cw) {
  1334.         cw.visitEnd();
  1335.         byte[] code = cw.toByteArray();
  1336.         if (DEBUG) CheckClassAdapter.verify(new ClassReader(code), false, new PrintWriter(System.err));

  1337.         return code;
  1338.     }
  1339.    
  1340.     private SkinnyMethodAdapter beginMethod(ClassWriter cw, String methodName, int specificArity, boolean block) {
  1341.         switch (specificArity) {
  1342.         default:
  1343.         case -1:
  1344.             if (block) {
  1345.                 return new SkinnyMethodAdapter(cw, ACC_PUBLIC, methodName, COMPILED_CALL_SIG_BLOCK, null, null);
  1346.             } else {
  1347.                 return new SkinnyMethodAdapter(cw, ACC_PUBLIC, methodName, COMPILED_CALL_SIG, null, null);
  1348.             }
  1349.         case 0:
  1350.             if (block) {
  1351.                 return new SkinnyMethodAdapter(cw, ACC_PUBLIC, methodName, COMPILED_CALL_SIG_ZERO_BLOCK, null, null);
  1352.             } else {
  1353.                 return new SkinnyMethodAdapter(cw, ACC_PUBLIC, methodName, COMPILED_CALL_SIG_ZERO, null, null);
  1354.             }
  1355.         case 1:
  1356.             if (block) {
  1357.                 return new SkinnyMethodAdapter(cw, ACC_PUBLIC, methodName, COMPILED_CALL_SIG_ONE_BLOCK, null, null);
  1358.             } else {
  1359.                 return new SkinnyMethodAdapter(cw, ACC_PUBLIC, methodName, COMPILED_CALL_SIG_ONE, null, null);
  1360.             }
  1361.         case 2:
  1362.             if (block) {
  1363.                 return new SkinnyMethodAdapter(cw, ACC_PUBLIC, methodName, COMPILED_CALL_SIG_TWO_BLOCK, null, null);
  1364.             } else {
  1365.                 return new SkinnyMethodAdapter(cw, ACC_PUBLIC, methodName, COMPILED_CALL_SIG_TWO, null, null);
  1366.             }
  1367.         case 3:
  1368.             if (block) {
  1369.                 return new SkinnyMethodAdapter(cw, ACC_PUBLIC, methodName, COMPILED_CALL_SIG_THREE_BLOCK, null, null);
  1370.             } else {
  1371.                 return new SkinnyMethodAdapter(cw, ACC_PUBLIC, methodName, COMPILED_CALL_SIG_THREE, null, null);
  1372.             }
  1373.         }
  1374.     }

  1375.     private void addAnnotatedMethodInvoker(ClassWriter cw, String callName, String superClass, List<JavaMethodDescriptor> descs) {
  1376.         for (JavaMethodDescriptor desc: descs) {
  1377.             int specificArity = -1;
  1378.             if (desc.optional == 0 && !desc.rest) {
  1379.                 if (desc.required == 0) {
  1380.                     if (desc.actualRequired <= 3) {
  1381.                         specificArity = desc.actualRequired;
  1382.                     } else {
  1383.                         specificArity = -1;
  1384.                     }
  1385.                 } else if (desc.required >= 0 && desc.required <= 3) {
  1386.                     specificArity = desc.required;
  1387.                 }
  1388.             }

  1389.             boolean hasBlock = desc.hasBlock;
  1390.             SkinnyMethodAdapter mv = null;

  1391.             mv = beginMethod(cw, callName, specificArity, hasBlock);
  1392.             mv.visitCode();

  1393.             createAnnotatedMethodInvocation(desc, mv, superClass, specificArity, hasBlock);

  1394.             mv.end();
  1395.         }
  1396.     }

  1397.     private void createAnnotatedMethodInvocation(JavaMethodDescriptor desc, SkinnyMethodAdapter method, String superClass, int specificArity, boolean block) {
  1398.         String typePath = desc.declaringClassPath;
  1399.         String javaMethodName = desc.name;

  1400.         checkArity(desc.anno, method, specificArity);
  1401.        
  1402.         CallConfiguration callConfig = CallConfiguration.getCallConfigByAnno(desc.anno);
  1403.         if (!callConfig.isNoop()) {
  1404.             invokeCallConfigPre(method, superClass, specificArity, block, callConfig);
  1405.         }

  1406.         int traceBoolIndex = -1;
  1407.         if (RubyInstanceConfig.FULL_TRACE_ENABLED) {
  1408.             // load and store trace enabled flag
  1409.             switch (specificArity) {
  1410.             case -1:
  1411.                 traceBoolIndex = ARGS_INDEX + (block ? 1 : 0) + 1;
  1412.                 break;
  1413.             case 0:
  1414.                 traceBoolIndex = ARGS_INDEX + (block ? 1 : 0);
  1415.                 break;
  1416.             default:
  1417.                 traceBoolIndex = ARGS_INDEX + specificArity + (block ? 1 : 0) + 1;
  1418.             }

  1419.             method.aload(1);
  1420.             method.invokevirtual(p(ThreadContext.class), "getRuntime", sig(Ruby.class));
  1421.             method.invokevirtual(p(Ruby.class), "hasEventHooks", sig(boolean.class));
  1422.             method.istore(traceBoolIndex);

  1423.             // call trace
  1424.             invokeCCallTrace(method, traceBoolIndex);
  1425.         }

  1426.         Label tryBegin = new Label();
  1427.         Label tryEnd = new Label();
  1428.         Label doFinally = new Label();

  1429.         if (!callConfig.isNoop()) {
  1430.             method.trycatch(tryBegin, tryEnd, doFinally, null);
  1431.         }
  1432.        
  1433.         method.label(tryBegin);
  1434.         {
  1435.             loadReceiver(typePath, desc, method);
  1436.            
  1437.             loadArguments(method, desc, specificArity);
  1438.            
  1439.             loadBlock(method, specificArity, block);

  1440.             if (Modifier.isStatic(desc.modifiers)) {
  1441.                 // static invocation
  1442.                 method.invokestatic(typePath, javaMethodName, desc.signature);
  1443.             } else {
  1444.                 // virtual invocation
  1445.                 method.invokevirtual(typePath, javaMethodName, desc.signature);
  1446.             }

  1447.             if (desc.getReturnClass() == void.class) {
  1448.                 // void return type, so we need to load a nil for returning below
  1449.                 method.aload(THREADCONTEXT_INDEX);
  1450.                 method.getfield(p(ThreadContext.class), "nil", ci(IRubyObject.class));
  1451.             }
  1452.         }
  1453.         method.label(tryEnd);
  1454.        
  1455.         // normal finally and exit
  1456.         {
  1457.             if (RubyInstanceConfig.FULL_TRACE_ENABLED) {
  1458.                 invokeCReturnTrace(method, traceBoolIndex);
  1459.             }
  1460.            
  1461.             if (!callConfig.isNoop()) {
  1462.                 invokeCallConfigPost(method, superClass, callConfig);
  1463.             }

  1464.             // return
  1465.             method.visitInsn(ARETURN);
  1466.         }
  1467.        
  1468.         // these are only needed if we have a non-noop call config
  1469.         if (!callConfig.isNoop()) {
  1470.             // finally handling for abnormal exit
  1471.             {
  1472.                 method.label(doFinally);
  1473.                
  1474.                 if (RubyInstanceConfig.FULL_TRACE_ENABLED) {
  1475.                     invokeCReturnTrace(method, traceBoolIndex);
  1476.                 }

  1477.                 //call post method stuff (exception raised)
  1478.                 if (!callConfig.isNoop()) {
  1479.                     invokeCallConfigPost(method, superClass, callConfig);
  1480.                 }

  1481.                 // rethrow exception
  1482.                 method.athrow(); // rethrow it
  1483.             }
  1484.         }
  1485.     }

  1486.     private void invokeCCallTrace(SkinnyMethodAdapter method, int traceBoolIndex) {
  1487.         method.aloadMany(0, 1); // method, threadContext
  1488.         method.iload(traceBoolIndex); // traceEnable
  1489.         method.aload(4); // invokedName
  1490.         method.invokevirtual(p(JavaMethod.class), "callTrace", sig(void.class, ThreadContext.class, boolean.class, String.class));
  1491.     }
  1492.    
  1493.     private void invokeCReturnTrace(SkinnyMethodAdapter method, int traceBoolIndex) {
  1494.         method.aloadMany(0, 1); // method, threadContext
  1495.         method.iload(traceBoolIndex); // traceEnable
  1496.         method.aload(4); // invokedName
  1497.         method.invokevirtual(p(JavaMethod.class), "returnTrace", sig(void.class, ThreadContext.class, boolean.class, String.class));
  1498.     }

  1499.     private void invokeTraceCompiledPre(SkinnyMethodAdapter mv, String superClass, int traceBoolIndex, String filename, int line) {
  1500.         mv.aloadMany(0, 1); // method, threadContext
  1501.         mv.iload(traceBoolIndex); // traceEnable
  1502.         mv.aload(4); // invokedName
  1503.         mv.ldc(filename);
  1504.         mv.ldc(line);
  1505.         mv.invokevirtual(superClass, "callTraceCompiled", sig(void.class, ThreadContext.class, boolean.class, String.class, String.class, int.class));
  1506.     }

  1507.     private void invokeTraceCompiledPost(SkinnyMethodAdapter mv, String superClass, int traceBoolIndex) {
  1508.         mv.aloadMany(0, 1); // method, threadContext
  1509.         mv.iload(traceBoolIndex); // traceEnable
  1510.         mv.aload(4); // invokedName
  1511.         mv.invokevirtual(superClass, "returnTraceCompiled", sig(void.class, ThreadContext.class, boolean.class, String.class));
  1512.     }
  1513. }