Start line:  
End line:  

Snippet Preview

Snippet HTML Code

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

 
 
     private static final Logger LOG = LoggerFactory.getLogger("InvokeDynamicMethodFactory");
     
     private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
    
    
 
     public InvokeDynamicMethodFactory(ClassLoader classLoader) {
         super(classLoader);
     }

    
 
     @Override
             RubyModule implementationClass,
             String rubyName,
             String javaName,
             Arity arity,
             Visibility visibility,
             StaticScope scope,
             Object scriptObject,
             CallConfiguration callConfig,
             ISourcePosition position,
             String parameterDesc) {
 
         return getCompiledMethod(implementationClassrubyNamejavaNamearityvisibilityscopescriptObjectcallConfigpositionparameterDesc);
     }

    
    @Override
            RubyModule implementationClass,
            String rubyName,
            String javaName,
            Arity arity,
            Visibility visibility,
            StaticScope scope,
            Object scriptObject,
            CallConfiguration callConfig,
            ISourcePosition position,
            String parameterDesc) {
        Class scriptClass = scriptObject.getClass();
        try {
            MethodHandle[] targets = new MethodHandle[5];
            SmartHandle directCall;
            int specificArity = -1;
            // acquire handle to the actual method body
            if (scope.getRestArg() >= 0 || scope.getOptionalArgs() > 0 || scope.getRequiredArgs() > 3) {
                // variable arity method (has optional, rest, or more args than we can splat)
                directCall = SmartBinder
                        .from(.prependArg("script"scriptClass))
                        .invokeStaticQuiet(scriptClassjavaName)
                        .bindTo(scriptObject);
            } else {
                // specific arity method (less than 4 required args only)
                specificArity = scope.getRequiredArgs();
                directCall = SmartBinder
                        .from([specificArity].prependArg("script"scriptClass))
                        .invokeStaticQuiet(scriptClassjavaName)
                        .bindTo(scriptObject);
            }
            // wrap with framing logic if needed
            if (!callConfig.isNoop()) {
                directCall = SmartHandle
                        .from(directCall.signature(), InvocationLinker.wrapWithFraming(directCall.signature(), callConfigimplementationClassrubyNamedirectCall.handle(), scope));
            }
            // provide a variable-arity path for specific-arity target
            SmartHandle variableCall;
            if (specificArity >= 0) {
                SmartHandle arityCheck = SmartBinder
                        .from()
                        .append(new String[]{"min""max"}, new Class[]{int.classint.class}, specificArityspecificArity)
                        .cast()
                        .invokeStaticQuiet(Arity.class"checkArgumentCount");
                variableCall = SmartBinder
                        .from()
                        .foldVoid(arityCheck)
                        .permute("script""context""self""block""args")
                        .spread("arg"specificArity)
                        .permute("script""context""self""arg*""block")
                        .invoke(directCall);
            } else {
                variableCall = directCall;
            }
            // TODO: tracing
            // pre-call trace
            if (.) {
            }
            
            if (specificArity >= 0) {
                targets[specificArity] = directCall.handle();
                targets[4] = variableCall.handle();
            } else {
                targets[4] = directCall.handle();
            }
            
            return new HandleMethod(implementationClassvisibilitycallConfigtargetsparameterDesc);
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }
    @Override
    public byte[] getCompiledMethodOffline(
            String rubyNameString javaNameString classNameString invokerPathArity arity,
            StaticScope scopeCallConfiguration callConfigString filenameint line) {
        throw new RuntimeException("no offline support for invokedynamic handles");
    }
    @Override
    public DynamicMethod getAnnotatedMethod(RubyModule implementationClassList<JavaMethodDescriptordescs) {
        JavaMethodDescriptor desc1 = descs.get(0);
        
        if (desc1.anno.frame()) {
            // super logic does not work yet because we need to take impl class
            // and method name from the DynamicMethod#call call, so punt to
            // generated class for now
            return super.getAnnotatedMethod(implementationClassdescs);
        }
        if (!Modifier.isPublic(desc1.getDeclaringClass().getModifiers())) {
            .warn("warning: binding non-public class {}; reflected handles won't work"desc1.declaringClassName);
        }
        DescriptorInfo info = new DescriptorInfo(descs);
        MethodHandle[] targets = buildAnnotatedMethodHandles(implementationClass.getRuntime(), descsimplementationClass);
        
        return new HandleMethod(implementationClassdesc1.anno.visibility(), CallConfiguration.getCallConfig(info.isFrame(), info.isScope()), targetsnull);
    }
    private MethodHandle[] buildAnnotatedMethodHandles(Ruby runtimeList<JavaMethodDescriptordescsRubyModule implementationClass) {
        MethodHandle[] targets = new MethodHandle[5];
        
        for (JavaMethodDescriptor descdescs) {
            int specificArity = -1;
            if (desc.optional == 0 && !desc.rest) {
                if (desc.required == 0) {
                    if (desc.actualRequired <= 3) {
                        specificArity = desc.actualRequired;
                    } else {
                        specificArity = -1;
                    }
                } else if (desc.required >= 0 && desc.required <= 3) {
                    specificArity = desc.required;
                }
            }
            String javaMethodName = desc.name;
            String rubyName;
            
            if (desc.anno.name() != null && desc.anno.name().length > 0) {
                // FIXME: Using this for super may super up the wrong name
                rubyName = desc.anno.name()[0];
            } else {
                rubyName = javaMethodName;
            }
//            checkArity(desc.anno, method, specificArity);
            SmartBinder targetBinder;
            SmartHandle target;
            Signature baseSignature;
            if (specificArity >= 0) {
                baseSignature = [specificArity];
            } else {
                baseSignature = ;
            }
            
            targetBinder = SmartBinder.from(baseSignature);
            
            MethodHandle returnFilter = null;
            boolean castReturn = false;
            if (desc.returnClass != IRubyObject.class) {
                if (desc.returnClass == void.class) {
                    returnFilter = MethodHandles.constant(IRubyObject.classruntime.getNil());
                } else {
                    castReturn = true;
                }
            }
            
            if (desc.isStatic) {
                if (desc.hasContext) {
                    if (desc.hasBlock) {
                        // straight through with no permutation necessary
                    } else {
                        targetBinder = targetBinder.exclude("block");
                    }
                } else {
                    if (desc.hasBlock) {
                        targetBinder = targetBinder.exclude("context");
                    } else {
                        targetBinder = targetBinder.exclude("context""block");
                    }
                }
                
                if (returnFilter != null) {
                    targetBinder = targetBinder
                            .filterReturn(returnFilter);
                } else if (castReturn) {
                    targetBinder = targetBinder
                            .castReturn(desc.returnClass);
                }
            } else {
                if (desc.hasContext) {
                    if (desc.hasBlock) {
                        targetBinder = targetBinder.permute("self""context""arg*""block");
                    } else {
                        targetBinder = targetBinder.permute("self""context""arg*");
                    }
                } else {
                    if (desc.hasBlock) {
                        targetBinder = targetBinder.permute("self""arg*""block");
                    } else {
                        targetBinder = targetBinder.permute("self""arg*");
                    }
                }
                
                if (returnFilter != null) {
                    targetBinder = targetBinder
                            .filterReturn(returnFilter);
                } else if (castReturn) {
                    targetBinder = targetBinder
                            .castReturn(desc.returnClass);
                }
                targetBinder = targetBinder
                        .castArg("self"desc.getDeclaringClass());
            }
            
            if (desc.isStatic) {
                target = targetBinder
                        .invokeStaticQuiet(desc.getDeclaringClass(), javaMethodName);
            } else {
                target = targetBinder
                        .invokeVirtualQuiet(javaMethodName);
            }
            CallConfiguration callConfig = CallConfiguration.getCallConfigByAnno(desc.anno);
            if (!callConfig.isNoop()) {
                target = SmartHandle
                        .from(target.signature(), InvocationLinker.wrapWithFraming(baseSignaturecallConfigimplementationClassrubyNametarget.handle(), null));
            }
            
            if (specificArity >= 0) {
                targets[specificArity] = target.handle();
            } else {
                targets[4] = target.handle();
            }
        }
        
        if (targets[4] == null) {
            // provide a variable-arity path for specific-arity target
            Signature VARIABLE_ARITY_SIGNATURE = Signature
                    .returning(IRubyObject.class)
                    .appendArg("context"ThreadContext.class)
                    .appendArg("self"IRubyObject.class)
                    .appendArg("args"IRubyObject[].class)
                    .appendArg("block"Block.class);
            
            // convert all specific-arity handles into varargs handles
            MethodHandle[] varargsTargets = new MethodHandle[4];
            for (int i = 0; i < 4; i++) {
                // TODO arity error
                if (targets[i] == nullcontinue;
                if (i == 0) {
                    varargsTargets[i] = MethodHandles.dropArguments(targets[i], 2, IRubyObject[].class);
                } else {
                    varargsTargets[i] = SmartBinder
                            .from(VARIABLE_ARITY_SIGNATURE)
                            .permute("context""self""block""args")
                            .spread("arg"i)
                            .permute("context""self""arg*""block")
                            .invoke(targets[i]).handle();
                }
            }
            
            SmartHandle HANDLE_GETTER = SmartBinder
                    .from(Signature.returning(MethodHandle.class).appendArg("targets"MethodHandle[].class).appendArg("arity"int.class))
                    .arrayGet();
            
            SmartHandle handleLookup = SmartBinder
                    .from(Signature.returning(MethodHandle.class).appendArg("args"IRubyObject[].class))
                    .filterReturn(HANDLE_GETTER.bindTo(varargsTargets))
                    .cast(int.classObject.class)
                    .invokeStaticQuiet(Array.class"getLength");
            
            SmartHandle variableCall = SmartBinder
                    .from(VARIABLE_ARITY_SIGNATURE)
                    .fold("handle"handleLookup)
                    .invoker();
            
            targets[4] = variableCall.handle();
        }
        
        
        // TODO: tracing
        
        return targets;
    }

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

    @Override
    public DynamicMethod getAnnotatedMethod(RubyModule implementationClassJavaMethodDescriptor desc) {
        return getAnnotatedMethod(implementationClass, Arrays.asList(desc));
    }
    
    public static final Signature VARIABLE_ARITY_SIGNATURE = Signature
            .returning(IRubyObject.class)
            .appendArg("context"ThreadContext.class)
            .appendArg("self"IRubyObject.class)
            .appendArg("args"IRubyObject[].class)
            .appendArg("block"Block.class);
    
    public static final Signature ARITY_CHECK_FOLD = Signature
            .returning(void.class)
            .appendArg("context"ThreadContext.class)
            .appendArg("args"IRubyObject[].class);
    
    public static final Signature ARITY_CHECK_SIGNATURE = Signature
            .returning(int.class)
            .appendArg("context"ThreadContext.class)
            .appendArg("args"IRubyObject[].class)
            .appendArg("min"int.class)
            .appendArg("max"int.class);
    
    public static final Signature[] SPECIFIC_ARITY_SIGNATURES;
    static {
            Signature[] specifics = new Signature[4];
            Signature specific = Signature
                    .returning(IRubyObject.class)
                    .appendArg("context"ThreadContext.class)
                    .appendArg("self"IRubyObject.class);
            
            specifics[0] = specific.appendArg("block"Block.class);
            
            for (int i = 0; i < 3; i++) {
                specific = specific
                        .appendArg("arg" + iIRubyObject.class);
                specifics[i + 1] = specific.appendArg("block"Block.class);
            }
             = specifics;
    }
    
    private static final SmartBinder[] SPREAD_BINDERS = new SmartBinder[4];
    static {
        for (int i = 0; i < 4; i++) {
            [i] = SmartBinder
                    .from()
                    .permute("context""self""block""args")
                    .spread("arg"i)
                    .permute("context""self""arg*""block");
        }
    }
    
    private static final SmartHandle HANDLE_GETTER = SmartBinder
                    .from(Signature.returning(MethodHandle.class).appendArg("targets"MethodHandle[].class).appendArg("arity"int.class))
                    .arrayGet();
New to GrepCode? Check out our FAQ X